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 [_cheatData process
];
186 if ( [_searchData hasSearchedOnce
] ) {
187 NSBeginInformationalAlertSheet( @
"Confirm target change.", @
"OK", @
"Cancel", nil, ibWindow
, self, NULL
,
188 @selector(_confirmTargetChange
:returnCode
:context
:), [[sender representedObject
] retain
],
189 @
"If you change the target now, your search will be cleared. This cannot be undone. Continue?" );
192 // request the change
193 [_cheater setTarget
:(Process
*)[sender representedObject
]];
197 - (void)_confirmTargetChange
:(NSWindow
*)sheet returnCode
:(int)returnCode context
:(void *)contextInfo
199 NSMenu
*processMenu
= [ibProcessPopup menu
];
200 Process
*process
= (Process
*)contextInfo
;
202 if ( returnCode
== NSAlertDefaultReturn
) {
204 [self ibClearSearch
:nil];
205 // request the change
206 [_cheater setTarget
:process
];
209 // select the correct server menuitem
210 [ibProcessPopup selectItemAtIndex
:[processMenu indexOfItemWithRepresentedObject
:_process
]];
217 - (IBAction
)ibSetVariableType
:(id)sender
219 [_searchData setVariableType
:[sender tag
]];
220 [self updateInterface
];
223 - (IBAction
)ibSetIntegerSign
:(id)sender
225 [_searchData setIntegerSign
:[[sender selectedCell
] tag
]];
228 - (IBAction
)ibSetOperator
:(id)sender
230 [_searchData setSearchOperator
:[sender tag
]];
233 - (IBAction
)ibSetValueUsed
:(id)sender
235 [_searchData setValueUsed
:[[sender selectedCell
] tag
]];
236 [self updateInterface
];
239 - (IBAction
)ibClearSearch
:(id)sender
241 [_cheater clearSearch
];
244 - (IBAction
)ibSearch
:(id)sender
249 if ( [_searchData valueUsed
] == TCGivenValue
) {
250 variable
= [[Variable alloc
] initWithType
:[_searchData variableType
] integerSign
:[_searchData integerSign
]];
251 [variable setProcess
:_process
];
252 [variable setStringValue
:[ibSearchValueField stringValue
]];
253 if ( [variable isValueValid
] && [variable valueSize
] > 0 ) {
254 _status
= TCSearchingStatus
;
255 [ibStatusText setDefaultStatus
:[NSString stringWithFormat
:@
"Searching %@'s memory%C", [_process name
], 0x2026]];
256 [ibStatusBar setIndeterminate
:NO
];
258 [_searchData setSearchValue
:variable
];
259 [_cheater searchForVariable
:variable comparison
:[_searchData searchOperator
]];
260 //[_cheater searchForVariable:[_searchData searchValue] comparison:[_searchData searchOperator]];
264 NSBeginAlertSheet( @
"Invalid Input", @
"OK", nil, nil, ibWindow
, nil, NULL
, NULL
, NULL
,
265 @
"The search value \"%@\" cannot be used with this type of search.", [ibSearchValueField stringValue
] );
269 _status
= TCSearchingStatus
;
270 [ibStatusText setDefaultStatus
:[NSString stringWithFormat
:@
"Searching %@'s memory%C", [_process name
], 0x2026]];
271 [ibStatusBar setIndeterminate
:NO
];
273 [_cheater searchLastValuesComparison
:[_searchData searchOperator
]];
276 [self updateInterface
];
279 - (IBAction
)ibAddSearchVariable
:(id)sender
284 // don't do anything if there is nothing selected
285 if ( [ibSearchVariableTable selectedRow
] == -1 ) {
289 rows
= [ibSearchVariableTable selectedRows
];
291 for ( i
= 0; i
< top
; i
++ ) {
292 int rowIndex
= [[rows objectAtIndex
:i
] unsignedIntValue
];
293 // transfer the search variable to the cheat data
294 [_cheatData addVariable
:[_searchData variableAtIndex
:rowIndex
]];
297 // update the variable table
298 [ibCheatVariableTable reloadData
];
300 if ( [[NSUserDefaults standardUserDefaults
] boolForKey
:TCSwitchVariablesPref
] ) {
301 [self switchToCheatMode
];
303 int rowIndex
= [_cheatData variableCount
]-1;
304 if ( MacOSXVersion() >= 0x1030 ) {
305 [ibCheatVariableTable selectRowIndexes
:[NSIndexSet indexSetWithIndex
:rowIndex
] byExtendingSelection
:NO
];
308 [ibCheatVariableTable selectRow
:rowIndex byExtendingSelection
:NO
];
310 // start editing the last added variable
311 if ( [[NSUserDefaults standardUserDefaults
] boolForKey
:TCAutoStartEditingVarsPref
] ) {
314 if ( MacOSXVersion() >= 0x1030 ) {
315 [ibCheatVariableTable selectRowIndexes
:[NSIndexSet indexSetWithIndexesInRange
:NSMakeRange(rowIndex
-top
+1,top
-1)]
316 byExtendingSelection
:YES
];
319 for ( i
= 1; i
< top
; i
++ ) {
320 [ibCheatVariableTable selectRow
:rowIndex
-i byExtendingSelection
:YES
];
323 [ibCheatVariableTable scrollRowToVisible
:rowIndex
];
324 [self ibRunEditVariablesSheet
:nil];
328 [ibCheatVariableTable editColumn
:[ibCheatVariableTable columnWithIdentifier
:@
"value"]
329 row
:rowIndex withEvent
:nil select
:YES
];
335 [self setDocumentChanged
];
336 [self updateInterface
];
340 - (IBAction
)ibSetCheatRepeats
:(id)sender
342 [_cheatData setRepeats
:[sender state
]];
345 [self setDocumentChanged
];
346 [self updateInterface
];
349 - (IBAction
)ibSetRepeatInterval
:(id)sender
351 [_cheatData setRepeatInterval
:[sender doubleValue
]];
354 [self setDocumentChanged
];
355 [self updateInterface
];
358 - (IBAction
)ibCheat
:(id)sender
360 _status
= TCCheatingStatus
;
361 [_cheater makeVariableChanges
:[_cheatData enabledVariables
] repeat
:[_cheatData repeats
] interval
:[_cheatData repeatInterval
]];
363 // update status description
364 if ( [_cheatData repeats
] ) {
365 [ibStatusText setDefaultStatus
:[NSString stringWithFormat
:@
"Applying cheats to %@%C", [_process name
], 0x2026]];
366 [ibStatusBar setIndeterminate
:YES
];
367 [ibStatusBar startAnimation
:self];
369 [self updateInterface
];
374 - (IBAction
)ibRunPropertiesSheet
:(id)sender
377 [ibWindowTitleField setStringValue
:[_cheatData windowTitle
]];
378 [ibCheatInfoField setStringValue
:[_cheatData cheatInfo
]];
381 [NSApp beginSheet
:ibPropertiesSheet modalForWindow
:ibWindow modalDelegate
:nil didEndSelector
:NULL contextInfo
:nil];
384 - (IBAction
)ibEndPropertiesSheet
:(id)sender
386 [ibPropertiesSheet orderOut
:sender
];
387 [NSApp endSheet
:ibPropertiesSheet returnCode
:0];
389 if ( [sender tag
] == 1 ) {
390 // do not update anything if nothing has changed
391 if ( [[ibWindowTitleField stringValue
] isEqualToString
:[_cheatData windowTitle
]] &&
392 [[ibCheatInfoField stringValue
] isEqualToString
:[_cheatData cheatInfo
]] ) {
396 [_cheatData setWindowTitle
:[ibWindowTitleField stringValue
]];
397 [_cheatData setCheatInfo
:[ibCheatInfoField stringValue
]];
399 [self setDocumentChanged
];
400 [self updateInterface
];
405 - (IBAction
)ibRunPasswordSheet
:(id)sender
410 - (IBAction
)ibEndPasswordSheet
:(id)sender
416 - (IBAction
)ibRunCustomServerSheet
:(id)sender
419 [ibServerField setStringValue
:@
""];
420 [ibPortField setStringValue
:[NSString stringWithFormat
:@
"%i", TCDefaultListenPort
]];
423 [NSApp beginSheet
:ibCustomServerSheet modalForWindow
:ibWindow modalDelegate
:nil didEndSelector
:NULL contextInfo
:nil];
426 - (IBAction
)ibEndCustomServerSheet
:(id)sender
428 NSString
*server
= [ibServerField stringValue
];
429 int port
= [[ibPortField stringValue
] intValue
];
431 ChazLog( @
"ibEndCustomServerSheet: %@:%i", server
, port
);
433 [ibCustomServerSheet orderOut
:sender
];
434 [NSApp endSheet
:ibCustomServerSheet returnCode
:0];
436 if ( [sender tag
] == 1 ) {
437 [self connectWithURL
:[NSString stringWithFormat
:@
"cheat://%@:%i", server
, port
]];
442 - (IBAction
)ibRunEditVariablesSheet
:(id)sender
444 int row
= [ibCheatVariableTable selectedRow
];
447 // must have selected items
452 var
= [_cheatData variableAtIndex
:row
];
455 [ibNewValueField setStringValue
:[var stringValue
]];
458 [NSApp beginSheet
:ibEditVariablesSheet modalForWindow
:ibWindow modalDelegate
:nil didEndSelector
:NULL contextInfo
:nil];
461 - (IBAction
)ibEndEditVariablesSheet
:(id)sender
463 NSString
*newValue
= [ibNewValueField stringValue
];
467 [ibEditVariablesSheet orderOut
:sender
];
468 [NSApp endSheet
:ibEditVariablesSheet returnCode
:0];
470 if ( [sender tag
] == 0 ) {
473 if ( [newValue isEqualToString
:@
""] ) {
477 rows
= [ibCheatVariableTable selectedRows
];
480 // change all selected variables with the new value
482 for ( i
= 0; i
< top
; i
++ ) {
483 Variable
*var
= [_cheatData variableAtIndex
:[[rows objectAtIndex
:i
] unsignedIntValue
]];
484 [var setStringValue
:newValue
];
488 [ibCheatVariableTable reloadData
];
490 [self setDocumentChanged
];
491 [self updateInterface
];
495 - (IBAction
)ibPauseTarget
:(id)sender
497 [_cheater pauseTarget
];
500 - (IBAction
)ibResumeTarget
:(id)sender
502 [_cheater resumeTarget
];
506 - (IBAction
)ibCancelSearch
:(id)sender
508 _isCancelingTask
= YES
;
509 [_cheater cancelSearch
];
511 [self updateInterface
];
514 - (IBAction
)ibStopCheat
:(id)sender
516 _isCancelingTask
= YES
;
517 [_cheater stopChangingVariables
];
519 [self updateInterface
];
523 - (IBAction
)ibDumpMemory
:(id)sender
525 _status
= TCDumpingStatus
;
526 [_cheater getMemoryDump
];
529 [ibStatusText setDefaultStatus
:[NSString stringWithFormat
:@
"Dumping %@'s memory%C", [_process name
], 0x2026]];
530 [ibStatusBar setIndeterminate
:YES
];
531 [ibStatusBar startAnimation
:self];
533 [self updateInterface
];
536 - (IBAction
)ibCancelDump
:(id)sender
538 _isCancelingTask
= YES
;
539 [_cheater cancelMemoryDump
];
541 [self updateInterface
];
545 - (IBAction
)ibAddCheatVariable
:(id)sender
547 ChazLog( @
"ibAddCheatVariable:" );
548 Variable
*var
= [[Variable alloc
] initWithType
:[sender tag
]];
549 // add the new variable to the doc data
550 [_cheatData addVariable
:var
];
552 // update the variable table
553 [ibCheatVariableTable reloadData
];
555 if ( [[NSUserDefaults standardUserDefaults
] boolForKey
:TCSwitchVariablesPref
] ) {
556 [self switchToCheatMode
];
558 int row
= [_cheatData variableCount
]-1;
559 if ( MacOSXVersion() >= 0x1030 ) {
560 [ibCheatVariableTable selectRowIndexes
:[NSIndexSet indexSetWithIndex
:row
] byExtendingSelection
:NO
];
563 [ibCheatVariableTable selectRow
:row byExtendingSelection
:NO
];
565 // start editing new variable
566 if ( [[NSUserDefaults standardUserDefaults
] boolForKey
:TCAutoStartEditingVarsPref
] ) {
567 [ibCheatVariableTable editColumn
:[ibCheatVariableTable columnWithIdentifier
:@
"address"] row
:row withEvent
:nil select
:YES
];
572 [self setDocumentChanged
];
573 [self updateInterface
];
576 - (IBAction
)ibSetVariableEnabled
:(id)sender
583 ChazLog( @
"ibSetVariableEnabled: %i", [sender selectedRow
] );
585 flag
= [[_cheatData variableAtIndex
:[ibCheatVariableTable selectedRow
]] isEnabled
];
587 rows
= [ibCheatVariableTable selectedRows
];
590 for ( i
= 0; i
< top
; i
++ ) {
591 Variable
*var
= [_cheatData variableAtIndex
:[[rows objectAtIndex
:i
] unsignedIntValue
]];
592 [var setEnabled
:!flag
];
596 [ibCheatVariableTable reloadData
];
597 [self setDocumentChanged
];
598 [self updateInterface
];
602 - (IBAction
)ibToggleSearchCheat
:(id)sender
604 if ( _mode
== TCCheatMode
) {
605 [self switchToSearchMode
];
607 else if ( _mode
== TCSearchMode
) {
608 [self switchToCheatMode
];
613 - (IBAction
)ibUndo
:(id)sender
618 - (IBAction
)ibRedo
:(id)sender