]> Dogcows Code - chaz/thecheat/blob - CheatDocument.m
The Cheat 1.2.4
[chaz/thecheat] / CheatDocument.m
1
2 // **********************************************************************
3 // The Cheat - A universal game cheater for Mac OS X
4 // (C) 2003-2005 Chaz McGarvey (BrokenZipper)
5 //
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)
9 // any later version.
10 //
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.
15 //
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.
19 //
20
21 #import "CheatDocument.h"
22
23
24 // GLOBALS
25 // service browsing globals
26 unsigned static _tc_document_count = 0;
27 NSNetServiceBrowser static *_tc_service_browser = nil;
28 NSMutableArray static *_tc_cheat_services = nil;
29 // global target
30 Process static *_tc_target = nil;
31
32
33 @interface CheatDocument ( PrivateAPI )
34
35 // mode switching
36 - (void)_switchTo:(NSView *)destination from:(NSView *)source;
37 // using the service browser
38 + (void)_documentCreated;
39 + (void)_documentDestroyed;
40 // service addition/removal
41 - (void)_cheatServiceFound:(NSNotification *)note;
42 - (void)_cheatServiceRemoved:(NSNotification *)note;
43 // interface
44 - (void)_setupInitialInterface;
45 // notifications
46 - (void)_displayValuesPrefChanged:(NSNotification *)note;
47 - (void)_windowOnTopPrefChanged:(NSNotification *)note;
48 - (void)_hitsDisplayedPrefChanged:(NSNotification *)note;
49
50 @end
51
52 @interface NSTableView ( PrivateAPI )
53
54 - (NSRange)_rowsInRectAssumingRowsCoverVisible:(NSRect)rect;
55
56 @end
57
58
59 @implementation CheatDocument
60
61
62 // #############################################################################
63 #pragma mark Initialization
64 // #############################################################################
65
66 - (id)init // designated
67 {
68 if ( self = [super init] )
69 {
70 NSNotificationCenter *nc= [NSNotificationCenter defaultCenter];
71
72 ChazLog( @"init doc %X", self );
73 [CheatDocument _documentCreated];
74
75 // register for service change notifications
76 [nc addObserver:self selector:@selector(_cheatServiceFound:) name:TCServiceFoundNote object:nil];
77 [nc addObserver:self selector:@selector(_cheatServiceRemoved:) name:TCServiceRemovedNote object:nil];
78
79 _cheatData = [[CheatData alloc] init];
80 _searchData = [[SearchData alloc] init];
81 [_searchData setProcess:_process];
82
83 // show search mode when documents are first created
84 _connectsOnOpen = YES;
85 [self setMode:TCSearchMode];
86 }
87 return self;
88 }
89
90 - (id)initWithContentsOfFile:(NSString *)fileName ofType:(NSString *)docType
91 {
92 if ( self = [super initWithContentsOfFile:fileName ofType:docType] )
93 {
94 // if document opened from a file, show cheat mode by default
95 [self setMode:TCCheatMode];
96 }
97 return self;
98 }
99
100 - (id)initWithContentsOfURL:(NSURL *)aURL ofType:(NSString *)docType
101 {
102 if ( self = [super initWithContentsOfURL:aURL ofType:docType] )
103 {
104 // if document opened from a URL, show cheat mode by default
105 [self setMode:TCCheatMode];
106 }
107 return self;
108 }
109
110 - (void)dealloc
111 {
112 ChazLog( @"dealloc doc %X", self );
113
114 // unregister observers
115 [(NSNotificationCenter *)[NSNotificationCenter defaultCenter] removeObserver:self];
116
117 [_cheater setDelegate:nil];
118 [self disconnectFromCheater];
119
120 [_cheatData release];
121 [_searchData release];
122
123 [_serverObject release];
124 [_process release];
125
126 // release the fade if one is occuring
127 [_fadeView removeFromSuperview];
128 [_fadeView release];
129
130 [CheatDocument _documentDestroyed];
131
132 [super dealloc];
133 }
134
135
136 // #############################################################################
137 #pragma mark Nib Loading
138 // #############################################################################
139
140 - (NSString *)windowNibName
141 {
142 return @"CheatDocument";
143 }
144
145 - (void)windowControllerDidLoadNib:(NSWindowController *)aController
146 {
147 [super windowControllerDidLoadNib:aController];
148
149 NSNotificationCenter *nc= [NSNotificationCenter defaultCenter];
150
151 // register for app launch/quit notifications
152 [nc addObserver:self selector:@selector(_displayValuesPrefChanged:) name:TCDisplayValuesChangedNote object:nil];
153 [nc addObserver:self selector:@selector(_windowOnTopPrefChanged:) name:TCWindowsOnTopChangedNote object:nil];
154 [nc addObserver:self selector:@selector(_hitsDisplayedPrefChanged:) name:TCHitsDisplayedChangedNote object:nil];
155
156 // setup window frame saving
157 [ibWindow useOptimizedDrawing:YES];
158 [ibWindow setFrameAutosaveName:@"TCCheatWindow"];
159
160 // set options
161 if ( [[NSUserDefaults standardUserDefaults] boolForKey:TCWindowsOnTopPref] )
162 {
163 [ibWindow setLevel:NSPopUpMenuWindowLevel];
164 }
165
166 // display one of the modes
167 if ( _mode == TCCheatMode ) {
168 [self _switchTo:ibCheatContentView from:ibPlaceView];
169 }
170 else if ( _mode == TCSearchMode ) {
171 [self _switchTo:ibSearchContentView from:ibPlaceView];
172 }
173
174 // configure the initial interface
175 [self _setupInitialInterface];
176
177 // update interface
178 [ibStatusText setDefaultStatus:[self defaultStatusString]];
179 [self updateInterface];
180
181 // automatically connect to the local cheater
182 if ( _connectsOnOpen ) {
183 [self ibSetLocalCheater:nil];
184 }
185
186 ChazLog( @"superview: %@", [[ibSearchVariableTable superview] superview] );
187 }
188
189
190 // #############################################################################
191 #pragma mark Handling Files
192 // #############################################################################
193
194 - (NSData *)dataRepresentationOfType:(NSString *)type
195 {
196 return [NSArchiver archivedDataWithRootObject:_cheatData];
197 }
198
199 - (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)type
200 {
201 [_cheatData release];
202 _cheatData = nil;
203
204 if ( [type isEqualToString:@"Cheat Document"] ) {
205 NS_DURING
206 _cheatData = [[NSUnarchiver unarchiveObjectWithData:data] retain];
207 NS_HANDLER
208 if ( !_cheatData ) {
209 // alert the user of the unparsable file
210 NSBeep();
211 NSRunAlertPanel( @"The Cheat can't read file.", @"The file \"%@\" can't be read. It is probably not a cheat file, or it may be corrupted.", @"OK", nil, nil, [self fileName] );
212 return NO;
213 }
214 NS_ENDHANDLER
215 }
216
217 [self updateInterface];
218
219 return YES;
220 }
221
222
223 // #############################################################################
224 #pragma mark Service Finding
225 // #############################################################################
226
227 + (NSArray *)cheatServices
228 {
229 return [NSArray arrayWithArray:_tc_cheat_services];
230 }
231
232
233 - (void)_cheatServiceFound:(NSNotification *)note
234 {
235 NSMenuItem *menuItem;
236 NSNetService *item = [note object];
237
238 // add the newly found service to the server popup
239 menuItem = [[NSMenuItem alloc] init];
240 [menuItem setTarget:self];
241 [menuItem setAction:@selector(ibSetRemoteCheater:)];
242 [menuItem setTitle:[item name]];
243 [menuItem setRepresentedObject:item];
244 [self addServer:menuItem];
245 [menuItem release];
246 }
247
248 - (void)_cheatServiceRemoved:(NSNotification *)note
249 {
250 NSNetService *item = [note object];
251
252 // remove the service from the menu
253 [self removeServerWithObject:item];
254 }
255
256
257 // using the service browser
258 + (void)_documentCreated
259 {
260 _tc_document_count++;
261
262 if ( _tc_document_count == 1 ) {
263 // first document created, so start the service browser
264 [_tc_service_browser stop];
265 [_tc_cheat_services release];
266 // create and setup the browser
267 _tc_service_browser = [[NSNetServiceBrowser alloc] init];
268 [_tc_service_browser setDelegate:self];
269 [_tc_service_browser searchForServicesOfType:@"_cheat._tcp." inDomain:@""];
270 // create the service array
271 _tc_cheat_services = [[NSMutableArray alloc] init];
272 }
273 }
274
275 + (void)_documentDestroyed
276 {
277 _tc_document_count--;
278
279 if ( _tc_document_count == 0 ) {
280 // last document destroyed, so stop the service browser
281 [_tc_service_browser stop];
282 [_tc_cheat_services release];
283 // set the globals to nil for safety
284 _tc_service_browser = nil;
285 _tc_cheat_services = nil;
286 }
287 }
288
289
290 // NSNetServiceBrowser delegate methods
291 + (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)browser
292 {
293 ChazLog( @"service browser will search" );
294 }
295
296 + (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)browser
297 {
298 // if the browser stops we assume it needs to die.
299 ChazLog( @"service browser did stop search" );
300 [browser release];
301 }
302
303 + (void)netServiceBrowser:(NSNetServiceBrowser *)browser didNotSearch:(NSDictionary *)errorDict
304 {
305 ChazLog( @"service browser failed with error code: %i", [[errorDict objectForKey:NSNetServicesErrorCode] intValue] );
306 }
307
308 + (void)netServiceBrowser:(NSNetServiceBrowser *)browser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing
309 {
310 ChazLog( @"service browser found service: %@", [aNetService name] );
311
312 // ignore if this is the local server.
313 if ( [[(AppController *)NSApp cheatServer] isListening] &&
314 [[aNetService name] isEqualToString:[[NSUserDefaults standardUserDefaults] objectForKey:TCBroadcastNamePref]] ) {
315 return;
316 }
317
318 [_tc_cheat_services addObject:aNetService];
319 // send a notification for the new service
320 [[NSNotificationCenter defaultCenter] postNotificationName:TCServiceFoundNote object:aNetService];
321 }
322
323 + (void)netServiceBrowser:(NSNetServiceBrowser *)browser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing
324 {
325 ChazLog( @"service browser removed service: %@", [aNetService name] );
326
327 [_tc_cheat_services removeObject:aNetService];
328 // send a notification for the new service
329 [[NSNotificationCenter defaultCenter] postNotificationName:TCServiceRemovedNote object:aNetService];
330 }
331
332
333 // #############################################################################
334 #pragma mark Changing Mode
335 // #############################################################################
336
337 - (void)setMode:(TCDocumentMode)mode
338 {
339 // if the nib isn't loaded, change the mode
340 if ( !ibWindow ) {
341 _mode = mode;
342 }
343 }
344
345
346 - (void)switchToCheatMode
347 {
348 NSResponder *responder = [ibWindow firstResponder];
349
350 if ( [responder isKindOfClass:[NSText class]] ) {
351 /* Since text views et al. make the field editor the first
352 responder, you have to take its delegate since that will
353 be set to the actual text view. */
354 responder = [(NSText *)responder delegate];
355 }
356
357 if ( _mode == TCCheatMode ) {
358 return;
359 }
360 _mode = TCCheatMode;
361 [self _switchTo:ibCheatContentView from:ibSearchContentView];
362
363 // update the next key view
364 [ibProcessPopup setNextKeyView:ibCheatVariableTable];
365 // update current key view
366 if ( !_lastResponder || _lastResponder == ibWindow ) {
367 // set default responder
368 [ibWindow makeFirstResponder:ibCheatVariableTable];
369 }
370 else {
371 [ibWindow makeFirstResponder:_lastResponder];
372 }
373 _lastResponder = responder;
374 }
375
376 - (void)switchToSearchMode
377 {
378 NSResponder *responder = [ibWindow firstResponder];
379
380 if ( [responder isKindOfClass:[NSText class]] ) {
381 responder = [(NSText *)responder delegate];
382 }
383
384 if ( _mode == TCSearchMode ) {
385 return;
386 }
387 _mode = TCSearchMode;
388 [self _switchTo:ibSearchContentView from:ibCheatContentView];
389
390 // update the next key view
391 [ibProcessPopup setNextKeyView:ibSearchTypePopup];
392 // update current key view
393 if ( !_lastResponder || _lastResponder == ibWindow ) {
394 [ibWindow makeFirstResponder:ibSearchValueField];
395 }
396 else {
397 [ibWindow makeFirstResponder:_lastResponder];
398 }
399 _lastResponder = responder;
400 }
401
402
403 - (void)_switchTo:(NSView *)destination from:(NSView *)source
404 {
405 NSView *contentView = [ibWindow contentView];
406 NSRect frame = [source frame];
407 NSImage *fadeImage = nil;
408
409 if ( gFadeAnimationDuration != 0.0 && [source lockFocusIfCanDraw] ) {
410 // draw the view to the representation
411 NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:[source bounds]];
412 [source unlockFocus];
413
414 // create the image object
415 fadeImage = [[NSImage alloc] initWithSize:frame.size];
416 [fadeImage addRepresentation:imageRep];
417
418 if ( _fadeView ) {
419 // remove the old fade view
420 [_fadeView removeFromSuperview];
421 [_fadeView release];
422 }
423
424 // create the new fade view and start the fade
425 _fadeView = [[FadeView alloc] initWithFrame:frame];
426 [_fadeView setAutoresizingMask:[source autoresizingMask]];
427 [_fadeView setDelegate:self];
428 [_fadeView setImage:fadeImage];
429 [_fadeView setFadeDuration:gFadeAnimationDuration];
430 [contentView addSubview:_fadeView];
431 [_fadeView startFadeAnimation];
432
433 [fadeImage release];
434 }
435
436 // update view size of incoming view
437 [destination setFrame:frame];
438 // replace the views
439 [contentView replaceSubview:source with:destination];
440 }
441
442
443 // FadeView Delegate
444 - (void)fadeViewFinishedAnimation:(FadeView *)theView
445 {
446 [_fadeView removeFromSuperview];
447 [_fadeView release];
448 _fadeView = nil;
449 }
450
451
452 // #############################################################################
453 #pragma mark Accessors
454 // #############################################################################
455
456 - (NSString *)defaultStatusString
457 {
458 if ( !_cheater ) {
459 return @"Not Connected";
460 }
461 return [NSString stringWithFormat:@"Connected to %@.", [_cheater hostAddress]];
462 }
463
464 - (BOOL)isLoadedFromFile
465 {
466 return ([self fileName] != nil);
467 }
468
469
470 - (void)addServer:(NSMenuItem *)item
471 {
472 NSMenu *serverMenu = [ibServerPopup menu];
473
474 if ( item ) {
475 if ( [serverMenu numberOfItems] <= 2 ) {
476 // separator line
477 [serverMenu addItem:[NSMenuItem separatorItem]];
478 }
479 [serverMenu addItem:item];
480 }
481 }
482
483 - (void)removeServerWithObject:(id)serverObject
484 {
485 NSMenu *serverMenu = [ibServerPopup menu];
486
487 if ( serverObject ) {
488 [serverMenu removeItemWithRepresentedObject:serverObject];
489 if ( [serverMenu numberOfItems] == 3 ) {
490 // separator line
491 [serverMenu removeItemAtIndex:2];
492 }
493 }
494 }
495
496
497 // #############################################################################
498 #pragma mark Interface
499 // #############################################################################
500
501 - (void)_setupInitialInterface
502 {
503 NSMenu *serverMenu;
504 NSMenuItem *menuItem;
505
506 NSArray *cheatServices;
507 unsigned i, len;
508
509 // create and set the server popup menu
510 serverMenu = [[NSMenu alloc] init];
511 [serverMenu setAutoenablesItems:YES];
512 // add menu items
513 // local connection item
514 menuItem = [[NSMenuItem alloc] init];
515 [menuItem setTarget:self];
516 [menuItem setAction:@selector(ibSetLocalCheater:)];
517 [menuItem setTitle:@"On This Computer"];
518 [menuItem setRepresentedObject:[NSNull null]];
519 [serverMenu addItem:menuItem];
520 [menuItem release];
521 // arbitrary connection item
522 menuItem = [[NSMenuItem alloc] init];
523 [menuItem setTarget:self];
524 [menuItem setAction:@selector(ibRunCustomServerSheet:)];
525 [menuItem setTitle:[NSString stringWithFormat:@"Other Server%C", 0x2026]];
526 [menuItem setKeyEquivalent:@"k"];
527 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
528 [serverMenu addItem:menuItem];
529 [menuItem release];
530 // set the menu
531 [ibServerPopup setMenu:serverMenu];
532 [serverMenu release];
533
534 // add current list of rendezvous services
535 cheatServices = [CheatDocument cheatServices];
536 len = [cheatServices count];
537 for ( i = 0; i < len; i++ ) {
538 NSNetService *item = [cheatServices objectAtIndex:i];
539 menuItem = [[NSMenuItem alloc] init];
540 [menuItem setTarget:self];
541 [menuItem setAction:@selector(ibSetRemoteCheater:)];
542 [menuItem setTitle:[item name]];
543 [menuItem setRepresentedObject:item];
544 [self addServer:menuItem];
545 [menuItem release];
546 }
547
548 [ibSearchVariableTable setDoubleAction:@selector(ibAddSearchVariable:)];
549 [ibSearchVariableTable setCanDelete:NO];
550
551 // BUG: for some reason IB doesn't like to set the default selection
552 // for an NSMatrix to anything but the first cell, so set this explicitly.
553 [ibSearchValueUsedMatrix selectCellWithTag:TCGivenValue];
554
555 // we use undoing/redoing for reverting search results
556 [self setHasUndoManager:NO];
557 }
558
559 - (void)updateInterface
560 {
561 if ( _cheatData )
562 {
563 // if there is cheat data, fill in the data information
564 [ibWindow setTitle:[self displayName]];
565 if ( [_cheatData process] ) {
566 if ( [[_cheatData cheatInfo] isEqualToString:@""] ) {
567 [ibCheatInfoText setStringValue:[NSString stringWithFormat:@"%@ %@",
568 [_cheatData gameName],
569 [_cheatData gameVersion]]];
570 }
571 else {
572 [ibCheatInfoText setStringValue:[NSString stringWithFormat:@"%@ %@ - %@",
573 [_cheatData gameName],
574 [_cheatData gameVersion],
575 [_cheatData cheatInfo]]];
576 }
577 }
578 else {
579 [ibCheatInfoText setStringValue:[_cheatData cheatInfo]];
580 }
581
582 [ibCheatRepeatButton setState:[_cheatData repeats]];
583 [ibCheatRepeatField setDoubleValue:[_cheatData repeatInterval]];
584 }
585
586 // if we're connected...
587 if ( _cheater )
588 {
589 if ( _status == TCIdleStatus )
590 {
591 // WINDOW
592 [ibServerPopup setEnabled:YES];
593 [ibProcessPopup setEnabled:YES];
594 // SEARCH MODE
595 [ibSearchValueUsedMatrix setEnabled:YES];
596 if ( [_searchData hasSearchedOnce] ) {
597 [ibSearchTypePopup setEnabled:NO];
598 [ibSearchIntegerSignMatrix setEnabled:NO];
599 [[ibSearchValueUsedMatrix cellWithTag:TCLastValue] setEnabled:YES];
600 if ( [_searchData valueUsed] == TCGivenValue ) {
601 [ibSearchValueField setEnabled:YES];
602 }
603 else {
604 [ibSearchValueField setEnabled:NO];
605 }
606 [ibSearchClearButton setEnabled:YES];
607 [ibSearchVariableTable setEnabled:YES];
608 int selectedRows = [ibSearchVariableTable numberOfSelectedRows];
609 if ( selectedRows > 0 ) {
610 [ibSearchVariableButton setEnabled:YES];
611 }
612 else {
613 [ibSearchVariableButton setEnabled:NO];
614 }
615 }
616 else {
617 [ibSearchTypePopup setEnabled:YES];
618 if ( [_searchData isTypeInteger] ) {
619 [ibSearchIntegerSignMatrix setEnabled:YES];
620 }
621 else {
622 [ibSearchIntegerSignMatrix setEnabled:NO];
623 }
624 [[ibSearchValueUsedMatrix cellWithTag:TCLastValue] setEnabled:NO];
625 [ibSearchValueUsedMatrix selectCellWithTag:[_searchData valueUsed]];
626 [ibSearchValueField setEnabled:YES];
627 [ibSearchClearButton setEnabled:NO];
628 [ibSearchVariableTable setEnabled:NO];
629 [ibSearchVariableButton setEnabled:NO];
630 }
631 if ( [_searchData variableType] != TCString ) {
632 [ibSearchOperatorPopup setEnabled:YES];
633 }
634 else {
635 [ibSearchOperatorPopup setEnabled:NO];
636 }
637 [ibSearchButton setTitle:@"Search"];
638 [ibSearchButton setAction:@selector(ibSearch:)];
639 [ibSearchButton setKeyEquivalent:@""];
640 [ibSearchButton setEnabled:YES];
641 // CHEAT MODE
642 [ibCheatVariableTable setEnabled:YES];
643 [ibCheatRepeatButton setEnabled:YES];
644 [ibCheatRepeatAuxText setTextColor:[NSColor controlTextColor]];
645 [ibCheatRepeatField setEnabled:[_cheatData repeats]];
646 [ibCheatButton setTitle:@"Apply Cheat"];
647 [ibCheatButton setAction:@selector(ibCheat:)];
648 [ibCheatButton setKeyEquivalent:@"\r"];
649 if ( [_cheatData enabledVariableCount] > 0 ) {
650 [ibCheatButton setEnabled:YES];
651 if ( [[_cheatData process] sameApplicationAs:_process] ) {
652 [ibCheatButton setKeyEquivalent:@"\r"];
653 }
654 else {
655 [ibCheatButton setKeyEquivalent:@""];
656 }
657 }
658 else {
659 [ibCheatButton setEnabled:NO];
660 }
661 }
662 else
663 {
664 // WINDOW
665 [ibServerPopup setEnabled:NO];
666 [ibProcessPopup setEnabled:NO];
667 // SEARCH MODE
668 [ibSearchTypePopup setEnabled:NO];
669 [ibSearchIntegerSignMatrix setEnabled:NO];
670 [ibSearchOperatorPopup setEnabled:NO];
671 [ibSearchValueUsedMatrix setEnabled:NO];
672 [ibSearchValueField setEnabled:NO];
673 [ibSearchClearButton setEnabled:NO];
674 [ibSearchVariableTable setEnabled:NO];
675 [ibSearchVariableButton setEnabled:NO];
676 // CHEAT MODE
677 [ibCheatVariableTable setEnabled:NO];
678 [ibCheatRepeatButton setEnabled:NO];
679 [ibCheatRepeatAuxText setTextColor:[NSColor disabledControlTextColor]];
680 [ibCheatRepeatField setEnabled:NO];
681
682 if ( _status == TCSearchingStatus ) {
683 [ibSearchButton setTitle:@"Cancel"];
684 [ibSearchButton setAction:@selector(ibCancelSearch:)];
685 [ibSearchButton setKeyEquivalent:@"\E"];
686 [ibSearchButton setEnabled:!_isCancelingTask];
687 [ibCheatButton setTitle:@"Apply Cheat"];
688 [ibCheatButton setAction:@selector(ibCheat:)];
689 if ( [[_cheatData process] sameApplicationAs:_process] ) {
690 [ibCheatButton setKeyEquivalent:@"\r"];
691 }
692 else {
693 [ibCheatButton setKeyEquivalent:@""];
694 }
695 [ibCheatButton setEnabled:NO];
696 }
697 else if ( _status == TCCheatingStatus ) {
698 [ibSearchButton setTitle:@"Search"];
699 [ibSearchButton setAction:@selector(ibSearch:)];
700 [ibSearchButton setKeyEquivalent:@""];
701 [ibSearchButton setEnabled:NO];
702 [ibCheatButton setTitle:@"Stop Cheat"];
703 [ibCheatButton setAction:@selector(ibStopCheat:)];
704 [ibCheatButton setKeyEquivalent:@"\E"];
705 [ibCheatButton setEnabled:!_isCancelingTask];
706 }
707 else {
708 [ibSearchButton setTitle:@"Search"];
709 [ibSearchButton setAction:@selector(ibSearch:)];
710 [ibSearchButton setKeyEquivalent:@""];
711 [ibSearchButton setEnabled:NO];
712 [ibCheatButton setTitle:@"Apply Cheat"];
713 [ibCheatButton setAction:@selector(ibCheat:)];
714 if ( [[_cheatData process] sameApplicationAs:_process] ) {
715 [ibCheatButton setKeyEquivalent:@"\r"];
716 }
717 else {
718 [ibCheatButton setKeyEquivalent:@""];
719 }
720 [ibCheatButton setEnabled:NO];
721 }
722 }
723 }
724 else
725 {
726 // WINDOW
727 [ibServerPopup setEnabled:YES];
728 [ibProcessPopup setEnabled:NO];
729 // SEARCH MODE
730 [ibSearchTypePopup setEnabled:NO];
731 [ibSearchIntegerSignMatrix setEnabled:NO];
732 [ibSearchOperatorPopup setEnabled:NO];
733 [ibSearchValueUsedMatrix setEnabled:NO];
734 [ibSearchValueField setEnabled:NO];
735 [ibSearchButton setEnabled:NO];
736 [ibSearchClearButton setEnabled:NO];
737 [ibSearchVariableTable setEnabled:NO];
738 [ibSearchVariableButton setEnabled:NO];
739 // CHEAT MODE
740 [ibCheatVariableTable setEnabled:NO];
741 [ibCheatRepeatButton setEnabled:NO];
742 [ibCheatRepeatAuxText setTextColor:[NSColor disabledControlTextColor]];
743 [ibCheatRepeatField setEnabled:NO];
744 [ibCheatButton setEnabled:NO];
745 }
746 }
747
748
749 - (void)setActualResults:(unsigned)count
750 {
751 unsigned recieved = [_searchData numberOfResults];
752
753 if ( count == 0 ) {
754 [ibSearchVariableTable setToolTip:@""];
755 }
756 else if ( recieved == count ) {
757 if ( count == 1 ) {
758 [ibSearchVariableTable setToolTip:[NSString stringWithFormat:@"Displaying one result."]];
759 }
760 else {
761 [ibSearchVariableTable setToolTip:[NSString stringWithFormat:@"Displaying %i results.", count]];
762 }
763 }
764 else if ( recieved < count ) {
765 [ibSearchVariableTable setToolTip:[NSString stringWithFormat:@"Displaying %i of %i results.", recieved, count]];
766 }
767 }
768
769
770 - (NSString *)displayName
771 {
772 // override the default window title if there is a custom one
773 NSString *title = [_cheatData windowTitle];
774
775 if ( !title || [title isEqualToString:@""] ) {
776 return [super displayName];
777 }
778 return title;
779 }
780
781 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
782 {
783 //ChazLog( @"validate menuitem: %@", [menuItem title] );
784
785 // the undo/redo
786 if ( [menuItem action] == @selector(ibUndo:) ) {
787 if ( [_searchData undoesLeft] > 0 ) {
788 return YES;
789 }
790 else {
791 return NO;
792 }
793 }
794 if ( [menuItem action] == @selector(ibRedo:) ) {
795 if ( [_searchData redoesLeft] > 0 ) {
796 return YES;
797 }
798 else {
799 return NO;
800 }
801 }
802
803 // the add variables items
804 if ( [menuItem action] == @selector(ibAddCheatVariable:) && _status == TCCheatingStatus ) {
805 return NO;
806 }
807
808 // the 'pause' menu item
809 if ( [menuItem tag] == 1000 ) {
810 if ( !_cheater ) {
811 return NO;
812 }
813 if ( _isTargetPaused ) {
814 [menuItem setTitle:@"Resume Target"];
815 [menuItem setAction:@selector(ibResumeTarget:)];
816 }
817 else {
818 [menuItem setTitle:@"Pause Target"];
819 [menuItem setAction:@selector(ibPauseTarget:)];
820 }
821 }
822
823 // the 'memory dump' menu item
824 else if ( [menuItem tag] == 1001 ) {
825 if ( !_cheater || _status != TCIdleStatus ) {
826 return NO;
827 }
828 }
829 // the 'mode switch' menu item
830 else if ( [menuItem tag] == 1002 ) {
831 if ( _mode == TCSearchMode ) {
832 [menuItem setTitle:@"Show Cheat Mode"];
833 }
834 else /* _mode == TCCheatMode */ {
835 [menuItem setTitle:@"Show Search Mode"];
836 }
837 }
838 // the 'edit variables' menu item
839 else if ( [menuItem tag] == 1003 ) {
840 return (_mode == TCCheatMode && [ibCheatVariableTable selectedRow] != -1);
841 }
842 // the 'clear search' menu item
843 else if ( [menuItem tag] == 1004 ) {
844 return [ibSearchClearButton isEnabled];
845 }
846 // the cancel menu item
847 else if ( [menuItem tag] == 1005 ) {
848 if ( !_cheater || _isCancelingTask ) {
849 return NO;
850 }
851 if ( _status == TCSearchingStatus ) {
852 [menuItem setTitle:@"Cancel Search"];
853 [menuItem setAction:@selector(ibCancelSearch:)];
854 }
855 else if ( _status == TCCheatingStatus ) {
856 [menuItem setTitle:@"Stop Cheat"];
857 [menuItem setAction:@selector(ibStopCheat:)];
858 }
859 else if ( _status == TCDumpingStatus ) {
860 [menuItem setTitle:@"Cancel Dump"];
861 [menuItem setAction:@selector(ibCancelDump:)];
862 }
863 else {
864 return NO;
865 }
866 }
867
868 return [super validateMenuItem:menuItem];
869 }
870
871
872 - (void)setDocumentChanged
873 {
874 // only count document changes if there are variables
875 // and if the pref is set
876 if ( [[NSUserDefaults standardUserDefaults] boolForKey:TCAskForSavePref] &&
877 ([_cheatData variableCount] > 0 || [self isLoadedFromFile]) ) {
878 [self updateChangeCount:NSChangeDone];
879 }
880 }
881
882
883 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
884 {
885 if ( aTableView == ibCheatVariableTable ) {
886 return [_cheatData variableCount];
887 }
888 else if ( aTableView == ibSearchVariableTable ) {
889 return [_searchData numberOfResults];
890 }
891 return 0;
892 }
893
894 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
895 {
896 NSString *identifier = [aTableColumn identifier];
897
898 if ( aTableView == ibCheatVariableTable ) {
899 Variable *variable = [_cheatData variableAtIndex:rowIndex];
900
901 if ( [identifier isEqualToString:@"enable"] ) {
902 return [NSNumber numberWithBool:[variable isEnabled]];
903 }
904 else if ( [identifier isEqualToString:@"variable"] ) {
905 return [variable typeString];
906 }
907 else if ( [identifier isEqualToString:@"address"] ) {
908 return [variable addressString];
909 }
910 else if ( [identifier isEqualToString:@"value"] ) {
911 return [variable stringValue];
912 }
913 }
914 else if ( aTableView == ibSearchVariableTable ) {
915 return [_searchData stringForRow:rowIndex];
916 }
917 return @"";
918 }
919
920 - (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
921 {
922 NSString *identifier = [aTableColumn identifier];
923
924 if ( aTableView == ibCheatVariableTable ) {
925 Variable *variable = [_cheatData variableAtIndex:rowIndex];
926
927 if ( [identifier isEqualToString:@"address"] ) {
928 if ( [variable setAddressString:anObject] ) {
929 [self setDocumentChanged];
930 }
931 }
932 else if ( [identifier isEqualToString:@"value"] ) {
933 if ( [variable setStringValue:anObject] ) {
934 [self setDocumentChanged];
935 }
936 }
937 }
938 }
939
940 - (void)tableViewSelectionDidChange:(NSNotification *)aNotification
941 {
942 NSTableView *aTableView = [aNotification object];
943
944 if ( aTableView == ibSearchVariableTable ) {
945 int selectedRows = [aTableView numberOfSelectedRows];
946 if ( selectedRows > 1 ) {
947 [ibSearchVariableButton setTitle:@"Add Variables"];
948 }
949 else if ( selectedRows == 1 ) {
950 [ibSearchVariableButton setTitle:@"Add Variable"];
951 }
952 }
953 }
954
955
956 - (BOOL)tableViewDidReceiveEnterKey:(NSTableView *)tableView
957 {
958 if ( tableView == ibSearchVariableTable ) {
959 [ibSearchVariableButton performClick:nil];
960 return YES;
961 }
962 return NO;
963 }
964
965 - (BOOL)tableViewDidReceiveSpaceKey:(NSTableView *)tableView
966 {
967 if ( tableView == ibCheatVariableTable ) {
968 [self ibSetVariableEnabled:nil];
969 return YES;
970 }
971 return NO;
972 }
973
974 - (NSString *)tableViewPasteboardType:(NSTableView *)tableView
975 {
976 return @"TCVariablePboardType";
977 }
978
979 - (NSData *)tableView:(NSTableView *)tableView copyRows:(NSArray *)rows
980 {
981 NSMutableArray *vars;
982 int i, top;
983
984 top = [rows count];
985 vars = [[NSMutableArray alloc] initWithCapacity:top];
986
987 // add the new variables
988 if ( tableView == ibSearchVariableTable ) {
989 for ( i = 0; i < top; i++ ) {
990 unsigned index = [[rows objectAtIndex:i] unsignedIntValue];
991 [vars addObject:[_searchData variableAtIndex:index]];
992 }
993 }
994 else {
995 for ( i = 0; i < top; i++ ) {
996 unsigned index = [[rows objectAtIndex:i] unsignedIntValue];
997 [vars addObject:[_cheatData variableAtIndex:index]];
998 }
999 }
1000
1001 return [NSArchiver archivedDataWithRootObject:[vars autorelease]];
1002 }
1003
1004 - (void)tableView:(NSTableView *)tableView pasteRowsWithData:(NSData *)rowData
1005 {
1006 NSArray *vars = [NSUnarchiver unarchiveObjectWithData:rowData];
1007 int i, top, lastRow;
1008
1009 if ( tableView == ibSearchVariableTable ) {
1010 NSBeep();
1011 return;
1012 }
1013
1014 top = [vars count];
1015 for ( i = 0; i < top; i++ ) {
1016 Variable *var = [vars objectAtIndex:i];
1017 [_cheatData addVariable:var];
1018 }
1019
1020 lastRow = [_cheatData variableCount]-1;
1021 [tableView reloadData];
1022 if ( MacOSXVersion() >= 0x1030 ) {
1023 [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:lastRow] byExtendingSelection:NO];
1024 }
1025 else {
1026 [tableView selectRow:lastRow byExtendingSelection:NO];
1027 }
1028 [tableView scrollRowToVisible:lastRow];
1029
1030 [self setDocumentChanged];
1031 [self updateInterface];
1032 }
1033
1034 - (void)tableView:(NSTableView *)tableView deleteRows:(NSArray *)rows
1035 {
1036 int i, len;
1037
1038 if ( tableView == ibCheatVariableTable ) {
1039 len = [rows count];
1040 for ( i = len-1; i >= 0; i-- ) {
1041 [_cheatData removeVariableAtIndex:[[rows objectAtIndex:i] unsignedIntValue]];
1042 }
1043 // reselect the last item if the selection is now invalid
1044 len = [_cheatData variableCount] - 1;
1045 if ( [tableView selectedRow] > len ) {
1046 if ( MacOSXVersion() >= 0x1030 ) {
1047 [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:len] byExtendingSelection:NO];
1048 }
1049 else {
1050 [tableView selectRow:len byExtendingSelection:NO];
1051 }
1052 }
1053 [tableView reloadData];
1054
1055 [self setDocumentChanged];
1056 [self updateInterface];
1057 }
1058 }
1059
1060 // VariableTable Delegate
1061 - (void)tableView:(NSTableView *)aTableView didChangeVisibleRows:(NSRange)rows
1062 {
1063 ChazLog( @"new visible rows: %@", NSStringFromRange( rows ) );
1064 if ( [_searchData valuesLoaded] ) {
1065 [self watchVariables];
1066 }
1067 }
1068
1069
1070 // #############################################################################
1071 #pragma mark Utility
1072 // #############################################################################
1073
1074 + (void)setGlobalTarget:(Process *)target
1075 {
1076 [target retain];
1077 [_tc_target release];
1078 _tc_target = target;
1079 }
1080
1081 + (Process *)globalTarget
1082 {
1083 return _tc_target;
1084 }
1085
1086
1087 - (void)showError:(NSString *)error
1088 {
1089 NSColor *red = [NSColor colorWithCalibratedRed:0.7 green:0.0 blue:0.0 alpha:1.0];
1090 NSBeep();
1091 [ibStatusText setDefaultStatus:[self defaultStatusString]];
1092 [ibStatusText setTemporaryStatus:[NSString stringWithFormat:@"Error: %@", error] color:red duration:7.0];
1093 }
1094
1095
1096 - (BOOL)shouldConnectWithServer:(NSMenuItem *)item
1097 {
1098 id serverObject;
1099
1100 if ( _resolvingService ) {
1101 // don't connect if a service is being resolved
1102 [ibServerPopup selectItemAtIndex:[ibServerPopup indexOfItemWithRepresentedObject:_resolvingService]];
1103 return NO;
1104 }
1105
1106 if ( [item respondsToSelector:@selector(representedObject)] ) {
1107 serverObject = [item representedObject];
1108 }
1109 else {
1110 serverObject = [NSNull null];
1111 }
1112
1113 if ( [_serverObject isEqual:serverObject] ) {
1114 // already connected, don't connect
1115 return NO;
1116 }
1117 return YES;
1118 }
1119
1120 - (void)selectConnectedCheater
1121 {
1122 int index = [ibServerPopup indexOfItemWithRepresentedObject:_serverObject];
1123 if ( index != -1 ) {
1124 [ibServerPopup selectItemAtIndex:index];
1125 }
1126 }
1127
1128 - (void)connectWithServer:(NSMenuItem *)item
1129 {
1130 id serverObject;
1131
1132 if ( [item respondsToSelector:@selector(representedObject)] ) {
1133 serverObject = [item representedObject];
1134 }
1135 else {
1136 serverObject = [NSNull null];
1137 }
1138
1139 // save a reference to the server object
1140 [serverObject retain];
1141 [_serverObject release];
1142 _serverObject = serverObject;
1143 }
1144
1145 - (void)disconnectFromCheater
1146 {
1147 NSMenu *blankMenu;
1148
1149 // don't do anything if we are already disconnected
1150 if ( !_cheater ) {
1151 return;
1152 }
1153
1154 _status = TCIdleStatus;
1155
1156 // clear the search
1157 [_searchData clearResults];
1158
1159 //[ibSearchVariableTable reloadData]; // this can cause a crash, so commenting it out for now.
1160 // clear the selected process
1161 [_process release];
1162 _process = nil;
1163 [_serverObject release];
1164 _serverObject = nil;
1165
1166 if ( ![self isLoadedFromFile] ) {
1167 [_cheatData setProcess:nil];
1168 }
1169
1170 // clear the process menu
1171 blankMenu = [[NSMenu alloc] initWithTitle:@""];
1172 [blankMenu addItemWithTitle:@"" action:NULL keyEquivalent:@""];
1173 [ibProcessPopup setMenu:blankMenu];
1174 [blankMenu release];
1175
1176 // kill the connection
1177 [_cheater disconnect];
1178 [_cheater release];
1179 _cheater = nil;
1180 }
1181
1182
1183 - (void)setConnectOnOpen:(BOOL)flag
1184 {
1185 _connectsOnOpen = flag;
1186 }
1187
1188 - (void)connectWithURL:(NSString *)url
1189 {
1190 NSMenu *serverMenu = [ibServerPopup menu];
1191 NSURL *theUrl = [NSURL URLWithString:url];
1192
1193 NSString *host;
1194 int port;
1195
1196 NSData *addrData;
1197 int indexIfAlreadyExists;
1198
1199 host = [theUrl host];
1200 port = [[theUrl port] intValue];
1201
1202 if ( !host ) {
1203 NSBeginInformationalAlertSheet( @"The Cheat can't parse the URL.", @"OK", nil, nil, ibWindow, self, NULL, NULL, NULL,
1204 @"The Cheat can't connect to the server because \"%@\" is not a valid URL.", url );
1205 goto FAILURE;
1206 }
1207
1208 // use default port number
1209 if ( !port ) {
1210 port = TCDefaultListenPort;
1211 }
1212
1213 addrData = [MySocket addressWithHost:host port:port];
1214 if ( !addrData ) {
1215 NSBeginInformationalAlertSheet( @"The Cheat can't find the server.", @"OK", nil, nil, ibWindow, self, NULL, NULL, NULL,
1216 @"The Cheat can't connect to the server \"%@\" because it can't be found.", host );
1217 goto FAILURE;
1218 }
1219
1220 indexIfAlreadyExists = [serverMenu indexOfItemWithRepresentedObject:addrData];
1221 if ( indexIfAlreadyExists == -1 ) {
1222 NSMenuItem *menuItem;
1223 // add the newly found service to the server popup
1224 menuItem = [[NSMenuItem alloc] init];
1225 [menuItem setTarget:self];
1226 [menuItem setAction:@selector(ibSetCustomCheater:)];
1227 [menuItem setTitle:[NSString stringWithFormat:@"%@:%i", host, port]];
1228 [menuItem setRepresentedObject:addrData];
1229 [self addServer:menuItem];
1230 // select new item
1231 [self ibSetCustomCheater:menuItem];
1232 // cleanup
1233 [menuItem release];
1234 }
1235 else {
1236 // select matching item
1237 [self ibSetCustomCheater:[serverMenu itemAtIndex:indexIfAlreadyExists]];
1238 }
1239
1240 FAILURE:;
1241 [self selectConnectedCheater];
1242 }
1243
1244
1245 - (void)watchVariables
1246 {
1247 NSRange range;
1248
1249 if ( [[NSUserDefaults standardUserDefaults] boolForKey:TCDisplayValuesPref] ) {
1250 float interval = [[NSUserDefaults standardUserDefaults] floatForKey:TCValueUpdatePref];
1251
1252 range = [ibSearchVariableTable visibleRows];
1253 [_cheater watchVariablesAtIndex:range.location count:range.length interval:interval];
1254 }
1255 else {
1256 [_cheater stopWatchingVariables];
1257 }
1258 }
1259
1260
1261 // #############################################################################
1262 #pragma mark Notifications
1263 // #############################################################################
1264
1265 - (void)_displayValuesPrefChanged:(NSNotification *)note
1266 {
1267 [self watchVariables];
1268 }
1269
1270 - (void)_windowOnTopPrefChanged:(NSNotification *)note
1271 {
1272 ChazLog( @"_windowOnTopPrefChanged" );
1273 if ( [[NSUserDefaults standardUserDefaults] boolForKey:TCWindowsOnTopPref] ) {
1274 [ibWindow setLevel:NSPopUpMenuWindowLevel];
1275 }
1276 else {
1277 [ibWindow setLevel:NSNormalWindowLevel];
1278 }
1279 }
1280
1281 - (void)_hitsDisplayedPrefChanged:(NSNotification *)note
1282 {
1283 // send preferences to the cheater
1284 [_cheater limitReturnedResults:[[NSUserDefaults standardUserDefaults] integerForKey:TCHitsDisplayedPref]];
1285 }
1286
1287
1288 @end
This page took 0.092814 seconds and 4 git commands to generate.