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