Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

1277 lines (1074 sloc) 34.788 kb
/*
* The Cheat - The legendary universal game trainer for Mac OS X.
* http://www.brokenzipper.com/trac/wiki/TheCheat
*
* Copyright (c) 2003-2011, Charles McGarvey et al.
*
* Distributable under the terms and conditions of the 2-clause BSD
* license; see the file COPYING for the legal text of the license.
*/
#import "CheatDocument.h"
// GLOBALS
// service browsing globals
unsigned static _tc_document_count = 0;
NSNetServiceBrowser static *_tc_service_browser = nil;
NSMutableArray static *_tc_cheat_services = nil;
// global target
Process static *_tc_target = nil;
@interface CheatDocument ( PrivateAPI )
// mode switching
- (void)_switchTo:(NSView *)destination from:(NSView *)source;
// using the service browser
+ (void)_documentCreated;
+ (void)_documentDestroyed;
// service addition/removal
- (void)_cheatServiceFound:(NSNotification *)note;
- (void)_cheatServiceRemoved:(NSNotification *)note;
// interface
- (void)_setupInitialInterface;
// notifications
- (void)_displayValuesPrefChanged:(NSNotification *)note;
- (void)_windowOnTopPrefChanged:(NSNotification *)note;
- (void)_hitsDisplayedPrefChanged:(NSNotification *)note;
@end
@interface NSTableView ( PrivateAPI )
- (NSRange)_rowsInRectAssumingRowsCoverVisible:(NSRect)rect;
@end
@implementation CheatDocument
// #############################################################################
#pragma mark Initialization
// #############################################################################
- (id)init // designated
{
if ( self = [super init] )
{
NSNotificationCenter *nc= [NSNotificationCenter defaultCenter];
ChazLog( @"init doc %X", self );
[CheatDocument _documentCreated];
// register for service change notifications
[nc addObserver:self selector:@selector(_cheatServiceFound:) name:TCServiceFoundNote object:nil];
[nc addObserver:self selector:@selector(_cheatServiceRemoved:) name:TCServiceRemovedNote object:nil];
_cheatData = [[CheatData alloc] init];
_searchData = [[SearchData alloc] init];
[_searchData setProcess:_process];
// show search mode when documents are first created
_connectsOnOpen = YES;
[self setMode:TCSearchMode];
}
return self;
}
- (id)initWithContentsOfFile:(NSString *)fileName ofType:(NSString *)docType
{
if ( self = [super initWithContentsOfFile:fileName ofType:docType] )
{
// if document opened from a file, show cheat mode by default
[self setMode:TCCheatMode];
}
return self;
}
- (id)initWithContentsOfURL:(NSURL *)aURL ofType:(NSString *)docType
{
if ( self = [super initWithContentsOfURL:aURL ofType:docType] )
{
// if document opened from a URL, show cheat mode by default
[self setMode:TCCheatMode];
}
return self;
}
- (void)dealloc
{
ChazLog( @"dealloc doc %X", self );
// unregister observers
[(NSNotificationCenter *)[NSNotificationCenter defaultCenter] removeObserver:self];
[_cheater setDelegate:nil];
[self disconnectFromCheater];
[_cheatData release];
[_searchData release];
[_serverObject release];
[_process release];
// release the fade if one is occuring
[_fadeView removeFromSuperview];
[_fadeView release];
[CheatDocument _documentDestroyed];
[super dealloc];
}
// #############################################################################
#pragma mark Nib Loading
// #############################################################################
- (NSString *)windowNibName
{
return @"CheatDocument";
}
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
[super windowControllerDidLoadNib:aController];
NSNotificationCenter *nc= [NSNotificationCenter defaultCenter];
// register for app launch/quit notifications
[nc addObserver:self selector:@selector(_displayValuesPrefChanged:) name:TCDisplayValuesChangedNote object:nil];
[nc addObserver:self selector:@selector(_windowOnTopPrefChanged:) name:TCWindowsOnTopChangedNote object:nil];
[nc addObserver:self selector:@selector(_hitsDisplayedPrefChanged:) name:TCHitsDisplayedChangedNote object:nil];
// setup window frame saving
[ibWindow useOptimizedDrawing:YES];
[ibWindow setFrameAutosaveName:@"TCCheatWindow"];
// set options
if ( [[NSUserDefaults standardUserDefaults] boolForKey:TCWindowsOnTopPref] )
{
[ibWindow setLevel:NSPopUpMenuWindowLevel];
}
// display one of the modes
if ( _mode == TCCheatMode ) {
[self _switchTo:ibCheatContentView from:ibPlaceView];
}
else if ( _mode == TCSearchMode ) {
[self _switchTo:ibSearchContentView from:ibPlaceView];
}
// configure the initial interface
[self _setupInitialInterface];
// update interface
[ibStatusText setDefaultStatus:[self defaultStatusString]];
[self updateInterface];
// automatically connect to the local cheater
if ( _connectsOnOpen ) {
[self ibSetLocalCheater:nil];
}
ChazLog( @"superview: %@", [[ibSearchVariableTable superview] superview] );
}
// #############################################################################
#pragma mark Handling Files
// #############################################################################
- (NSData *)dataRepresentationOfType:(NSString *)type
{
return [NSArchiver archivedDataWithRootObject:_cheatData];
}
- (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)type
{
[_cheatData release];
_cheatData = nil;
if ( [type isEqualToString:@"Cheat Document"] ) {
_cheatData = [[NSUnarchiver unarchiveObjectWithData:data] retain];
if ( !_cheatData ) {
// alert the user of the unparsable file
NSBeep();
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] );
return NO;
}
}
[self updateInterface];
return YES;
}
// #############################################################################
#pragma mark Service Finding
// #############################################################################
+ (NSArray *)cheatServices
{
return [NSArray arrayWithArray:_tc_cheat_services];
}
- (void)_cheatServiceFound:(NSNotification *)note
{
NSMenuItem *menuItem;
NSNetService *item = [note object];
// add the newly found service to the server popup
menuItem = [[NSMenuItem alloc] init];
[menuItem setTarget:self];
[menuItem setAction:@selector(ibSetRemoteCheater:)];
[menuItem setTitle:[item name]];
[menuItem setRepresentedObject:item];
[self addServer:menuItem];
[menuItem release];
}
- (void)_cheatServiceRemoved:(NSNotification *)note
{
NSNetService *item = [note object];
// remove the service from the menu
[self removeServerWithObject:item];
}
// using the service browser
+ (void)_documentCreated
{
_tc_document_count++;
if ( _tc_document_count == 1 ) {
// first document created, so start the service browser
[_tc_service_browser stop];
[_tc_cheat_services release];
// create and setup the browser
_tc_service_browser = [[NSNetServiceBrowser alloc] init];
[_tc_service_browser setDelegate:self];
[_tc_service_browser searchForServicesOfType:@"_cheat._tcp." inDomain:@""];
// create the service array
_tc_cheat_services = [[NSMutableArray alloc] init];
}
}
+ (void)_documentDestroyed
{
_tc_document_count--;
if ( _tc_document_count == 0 ) {
// last document destroyed, so stop the service browser
[_tc_service_browser stop];
[_tc_cheat_services release];
// set the globals to nil for safety
_tc_service_browser = nil;
_tc_cheat_services = nil;
}
}
// NSNetServiceBrowser delegate methods
+ (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)browser
{
ChazLog( @"service browser will search" );
}
+ (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)browser
{
// if the browser stops we assume it needs to die.
ChazLog( @"service browser did stop search" );
[browser release];
}
+ (void)netServiceBrowser:(NSNetServiceBrowser *)browser didNotSearch:(NSDictionary *)errorDict
{
ChazLog( @"service browser failed with error code: %i", [[errorDict objectForKey:NSNetServicesErrorCode] intValue] );
}
+ (void)netServiceBrowser:(NSNetServiceBrowser *)browser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing
{
ChazLog( @"service browser found service: %@", [aNetService name] );
// ignore if this is the local server.
if ( [[(AppController *)NSApp cheatServer] isListening] &&
[[aNetService name] isEqualToString:[[NSUserDefaults standardUserDefaults] objectForKey:TCBroadcastNamePref]] ) {
return;
}
[_tc_cheat_services addObject:aNetService];
// send a notification for the new service
[[NSNotificationCenter defaultCenter] postNotificationName:TCServiceFoundNote object:aNetService];
}
+ (void)netServiceBrowser:(NSNetServiceBrowser *)browser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing
{
ChazLog( @"service browser removed service: %@", [aNetService name] );
[_tc_cheat_services removeObject:aNetService];
// send a notification for the new service
[[NSNotificationCenter defaultCenter] postNotificationName:TCServiceRemovedNote object:aNetService];
}
// #############################################################################
#pragma mark Changing Mode
// #############################################################################
- (void)setMode:(TCDocumentMode)mode
{
// if the nib isn't loaded, change the mode
if ( !ibWindow ) {
_mode = mode;
}
}
- (void)switchToCheatMode
{
NSResponder *responder = [ibWindow firstResponder];
if ( [responder isKindOfClass:[NSText class]] ) {
/* Since text views et al. make the field editor the first
responder, you have to take its delegate since that will
be set to the actual text view. */
responder = [(NSText *)responder delegate];
}
if ( _mode == TCCheatMode ) {
return;
}
_mode = TCCheatMode;
[self _switchTo:ibCheatContentView from:ibSearchContentView];
// update the next key view
[ibProcessPopup setNextKeyView:ibCheatVariableTable];
// update current key view
if ( !_lastResponder || _lastResponder == ibWindow ) {
// set default responder
[ibWindow makeFirstResponder:ibCheatVariableTable];
}
else {
[ibWindow makeFirstResponder:_lastResponder];
}
_lastResponder = responder;
}
- (void)switchToSearchMode
{
NSResponder *responder = [ibWindow firstResponder];
if ( [responder isKindOfClass:[NSText class]] ) {
responder = [(NSText *)responder delegate];
}
if ( _mode == TCSearchMode ) {
return;
}
_mode = TCSearchMode;
[self _switchTo:ibSearchContentView from:ibCheatContentView];
// update the next key view
[ibProcessPopup setNextKeyView:ibSearchTypePopup];
// update current key view
if ( !_lastResponder || _lastResponder == ibWindow ) {
[ibWindow makeFirstResponder:ibSearchValueField];
}
else {
[ibWindow makeFirstResponder:_lastResponder];
}
_lastResponder = responder;
}
- (void)_switchTo:(NSView *)destination from:(NSView *)source
{
NSView *contentView = [ibWindow contentView];
NSRect frame = [source frame];
NSImage *fadeImage = nil;
if ( gFadeAnimationDuration != 0.0 && [source lockFocusIfCanDraw] ) {
// draw the view to the representation
NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:[source bounds]];
[source unlockFocus];
// create the image object
fadeImage = [[NSImage alloc] initWithSize:frame.size];
[fadeImage addRepresentation:imageRep];
if ( _fadeView ) {
// remove the old fade view
[_fadeView removeFromSuperview];
[_fadeView release];
}
// create the new fade view and start the fade
_fadeView = [[FadeView alloc] initWithFrame:frame];
[_fadeView setAutoresizingMask:[source autoresizingMask]];
[_fadeView setDelegate:self];
[_fadeView setImage:fadeImage];
[_fadeView setFadeDuration:gFadeAnimationDuration];
[contentView addSubview:_fadeView];
[_fadeView startFadeAnimation];
[fadeImage release];
}
// update view size of incoming view
[destination setFrame:frame];
// replace the views
[contentView replaceSubview:source with:destination];
}
// FadeView Delegate
- (void)fadeViewFinishedAnimation:(FadeView *)theView
{
[_fadeView removeFromSuperview];
[_fadeView release];
_fadeView = nil;
}
// #############################################################################
#pragma mark Accessors
// #############################################################################
- (NSString *)defaultStatusString
{
if ( !_cheater ) {
return @"Not Connected";
}
return [NSString stringWithFormat:@"Connected to %@.", [_cheater hostAddress]];
}
- (BOOL)isLoadedFromFile
{
return ([self fileName] != nil);
}
- (void)addServer:(NSMenuItem *)item
{
NSMenu *serverMenu = [ibServerPopup menu];
if ( item ) {
if ( [serverMenu numberOfItems] <= 2 ) {
// separator line
[serverMenu addItem:[NSMenuItem separatorItem]];
}
[serverMenu addItem:item];
}
}
- (void)removeServerWithObject:(id)serverObject
{
NSMenu *serverMenu = [ibServerPopup menu];
if ( serverObject ) {
[serverMenu removeItemWithRepresentedObject:serverObject];
if ( [serverMenu numberOfItems] == 3 ) {
// separator line
[serverMenu removeItemAtIndex:2];
}
}
}
// #############################################################################
#pragma mark Interface
// #############################################################################
- (void)_setupInitialInterface
{
NSMenu *serverMenu;
NSMenuItem *menuItem;
NSArray *cheatServices;
unsigned i, len;
// create and set the server popup menu
serverMenu = [[NSMenu alloc] init];
[serverMenu setAutoenablesItems:YES];
// add menu items
// local connection item
menuItem = [[NSMenuItem alloc] init];
[menuItem setTarget:self];
[menuItem setAction:@selector(ibSetLocalCheater:)];
[menuItem setTitle:@"On This Computer"];
[menuItem setRepresentedObject:[NSNull null]];
[serverMenu addItem:menuItem];
[menuItem release];
// arbitrary connection item
menuItem = [[NSMenuItem alloc] init];
[menuItem setTarget:self];
[menuItem setAction:@selector(ibRunCustomServerSheet:)];
[menuItem setTitle:[NSString stringWithFormat:@"Other Server%C", 0x2026]];
[menuItem setKeyEquivalent:@"k"];
[menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
[serverMenu addItem:menuItem];
[menuItem release];
// set the menu
[ibServerPopup setMenu:serverMenu];
[serverMenu release];
// add current list of rendezvous services
cheatServices = [CheatDocument cheatServices];
len = [cheatServices count];
for ( i = 0; i < len; i++ ) {
NSNetService *item = [cheatServices objectAtIndex:i];
menuItem = [[NSMenuItem alloc] init];
[menuItem setTarget:self];
[menuItem setAction:@selector(ibSetRemoteCheater:)];
[menuItem setTitle:[item name]];
[menuItem setRepresentedObject:item];
[self addServer:menuItem];
[menuItem release];
}
[ibSearchVariableTable setDoubleAction:@selector(ibAddSearchVariable:)];
[ibSearchVariableTable setCanDelete:NO];
// BUG: for some reason IB doesn't like to set the default selection
// for an NSMatrix to anything but the first cell, so set this explicitly.
[ibSearchValueUsedMatrix selectCellWithTag:TCGivenValue];
// we use undoing/redoing for reverting search results
[self setHasUndoManager:NO];
}
- (void)updateInterface
{
if ( _cheatData )
{
// if there is cheat data, fill in the data information
[ibWindow setTitle:[self displayName]];
if ( [_cheatData process] ) {
if ( [[_cheatData cheatInfo] isEqualToString:@""] ) {
[ibCheatInfoText setStringValue:[NSString stringWithFormat:@"%@ %@",
[_cheatData gameName],
[_cheatData gameVersion]]];
}
else {
[ibCheatInfoText setStringValue:[NSString stringWithFormat:@"%@ %@ - %@",
[_cheatData gameName],
[_cheatData gameVersion],
[_cheatData cheatInfo]]];
}
}
else {
[ibCheatInfoText setStringValue:[_cheatData cheatInfo]];
}
[ibCheatRepeatButton setState:[_cheatData repeats]];
[ibCheatRepeatField setDoubleValue:[_cheatData repeatInterval]];
}
// if we're connected...
if ( _cheater )
{
if ( _status == TCIdleStatus )
{
// WINDOW
[ibServerPopup setEnabled:YES];
[ibProcessPopup setEnabled:YES];
// SEARCH MODE
[ibSearchValueUsedMatrix setEnabled:YES];
if ( [_searchData hasSearchedOnce] ) {
[ibSearchTypePopup setEnabled:NO];
[ibSearchIntegerSignMatrix setEnabled:NO];
[[ibSearchValueUsedMatrix cellWithTag:TCLastValue] setEnabled:YES];
if ( [_searchData valueUsed] == TCGivenValue ) {
[ibSearchValueField setEnabled:YES];
}
else {
[ibSearchValueField setEnabled:NO];
}
[ibSearchClearButton setEnabled:YES];
[ibSearchVariableTable setEnabled:YES];
int selectedRows = [ibSearchVariableTable numberOfSelectedRows];
if ( selectedRows > 0 ) {
[ibSearchVariableButton setEnabled:YES];
}
else {
[ibSearchVariableButton setEnabled:NO];
}
}
else {
[ibSearchTypePopup setEnabled:YES];
if ( [_searchData isTypeInteger] ) {
[ibSearchIntegerSignMatrix setEnabled:YES];
}
else {
[ibSearchIntegerSignMatrix setEnabled:NO];
}
[[ibSearchValueUsedMatrix cellWithTag:TCLastValue] setEnabled:NO];
[ibSearchValueUsedMatrix selectCellWithTag:[_searchData valueUsed]];
[ibSearchValueField setEnabled:YES];
[ibSearchClearButton setEnabled:NO];
[ibSearchVariableTable setEnabled:NO];
[ibSearchVariableButton setEnabled:NO];
}
if ( [_searchData variableType] != TCString ) {
[ibSearchOperatorPopup setEnabled:YES];
}
else {
[ibSearchOperatorPopup setEnabled:NO];
}
[ibSearchButton setTitle:@"Search"];
[ibSearchButton setAction:@selector(ibSearch:)];
[ibSearchButton setKeyEquivalent:@""];
[ibSearchButton setEnabled:YES];
// CHEAT MODE
[ibCheatVariableTable setEnabled:YES];
[ibCheatRepeatButton setEnabled:YES];
[ibCheatRepeatAuxText setTextColor:[NSColor controlTextColor]];
[ibCheatRepeatField setEnabled:[_cheatData repeats]];
[ibCheatButton setTitle:@"Apply Cheat"];
[ibCheatButton setAction:@selector(ibCheat:)];
[ibCheatButton setKeyEquivalent:@"\r"];
if ( [_cheatData enabledVariableCount] > 0 ) {
[ibCheatButton setEnabled:YES];
if ( [[_cheatData process] sameApplicationAs:_process] ) {
[ibCheatButton setKeyEquivalent:@"\r"];
}
else {
[ibCheatButton setKeyEquivalent:@""];
}
}
else {
[ibCheatButton setEnabled:NO];
}
}
else
{
// WINDOW
[ibServerPopup setEnabled:NO];
[ibProcessPopup setEnabled:NO];
// SEARCH MODE
[ibSearchTypePopup setEnabled:NO];
[ibSearchIntegerSignMatrix setEnabled:NO];
[ibSearchOperatorPopup setEnabled:NO];
[ibSearchValueUsedMatrix setEnabled:NO];
[ibSearchValueField setEnabled:NO];
[ibSearchClearButton setEnabled:NO];
[ibSearchVariableTable setEnabled:NO];
[ibSearchVariableButton setEnabled:NO];
// CHEAT MODE
[ibCheatVariableTable setEnabled:NO];
[ibCheatRepeatButton setEnabled:NO];
[ibCheatRepeatAuxText setTextColor:[NSColor disabledControlTextColor]];
[ibCheatRepeatField setEnabled:NO];
if ( _status == TCSearchingStatus ) {
[ibSearchButton setTitle:@"Cancel"];
[ibSearchButton setAction:@selector(ibCancelSearch:)];
[ibSearchButton setKeyEquivalent:@"\E"];
[ibSearchButton setEnabled:!_isCancelingTask];
[ibCheatButton setTitle:@"Apply Cheat"];
[ibCheatButton setAction:@selector(ibCheat:)];
if ( [[_cheatData process] sameApplicationAs:_process] ) {
[ibCheatButton setKeyEquivalent:@"\r"];
}
else {
[ibCheatButton setKeyEquivalent:@""];
}
[ibCheatButton setEnabled:NO];
}
else if ( _status == TCCheatingStatus ) {
[ibSearchButton setTitle:@"Search"];
[ibSearchButton setAction:@selector(ibSearch:)];
[ibSearchButton setKeyEquivalent:@""];
[ibSearchButton setEnabled:NO];
[ibCheatButton setTitle:@"Stop Cheat"];
[ibCheatButton setAction:@selector(ibStopCheat:)];
[ibCheatButton setKeyEquivalent:@"\E"];
[ibCheatButton setEnabled:!_isCancelingTask];
}
else {
[ibSearchButton setTitle:@"Search"];
[ibSearchButton setAction:@selector(ibSearch:)];
[ibSearchButton setKeyEquivalent:@""];
[ibSearchButton setEnabled:NO];
[ibCheatButton setTitle:@"Apply Cheat"];
[ibCheatButton setAction:@selector(ibCheat:)];
if ( [[_cheatData process] sameApplicationAs:_process] ) {
[ibCheatButton setKeyEquivalent:@"\r"];
}
else {
[ibCheatButton setKeyEquivalent:@""];
}
[ibCheatButton setEnabled:NO];
}
}
}
else
{
// WINDOW
[ibServerPopup setEnabled:YES];
[ibProcessPopup setEnabled:NO];
// SEARCH MODE
[ibSearchTypePopup setEnabled:NO];
[ibSearchIntegerSignMatrix setEnabled:NO];
[ibSearchOperatorPopup setEnabled:NO];
[ibSearchValueUsedMatrix setEnabled:NO];
[ibSearchValueField setEnabled:NO];
[ibSearchButton setEnabled:NO];
[ibSearchClearButton setEnabled:NO];
[ibSearchVariableTable setEnabled:NO];
[ibSearchVariableButton setEnabled:NO];
// CHEAT MODE
[ibCheatVariableTable setEnabled:NO];
[ibCheatRepeatButton setEnabled:NO];
[ibCheatRepeatAuxText setTextColor:[NSColor disabledControlTextColor]];
[ibCheatRepeatField setEnabled:NO];
[ibCheatButton setEnabled:NO];
}
}
- (void)setActualResults:(unsigned)count
{
unsigned recieved = [_searchData numberOfResults];
if ( count == 0 ) {
[ibSearchVariableTable setToolTip:@""];
}
else if ( recieved == count ) {
if ( count == 1 ) {
[ibSearchVariableTable setToolTip:[NSString stringWithFormat:@"Displaying one result."]];
}
else {
[ibSearchVariableTable setToolTip:[NSString stringWithFormat:@"Displaying %i results.", count]];
}
}
else if ( recieved < count ) {
[ibSearchVariableTable setToolTip:[NSString stringWithFormat:@"Displaying %i of %i results.", recieved, count]];
}
}
- (NSString *)displayName
{
// override the default window title if there is a custom one
NSString *title = [_cheatData windowTitle];
if ( !title || [title isEqualToString:@""] ) {
return [super displayName];
}
return title;
}
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
{
//ChazLog( @"validate menuitem: %@", [menuItem title] );
// the undo/redo
if ( [menuItem action] == @selector(ibUndo:) ) {
if ( [_searchData undoesLeft] > 0 ) {
return YES;
}
else {
return NO;
}
}
if ( [menuItem action] == @selector(ibRedo:) ) {
if ( [_searchData redoesLeft] > 0 ) {
return YES;
}
else {
return NO;
}
}
// the add variables items
if ( [menuItem action] == @selector(ibAddCheatVariable:) && _status == TCCheatingStatus ) {
return NO;
}
// the 'pause' menu item
if ( [menuItem tag] == 1000 ) {
if ( !_cheater ) {
return NO;
}
if ( _isTargetPaused ) {
[menuItem setTitle:@"Resume Target"];
[menuItem setAction:@selector(ibResumeTarget:)];
}
else {
[menuItem setTitle:@"Pause Target"];
[menuItem setAction:@selector(ibPauseTarget:)];
}
}
// the 'memory dump' menu item
else if ( [menuItem tag] == 1001 ) {
if ( !_cheater || _status != TCIdleStatus ) {
return NO;
}
}
// the 'mode switch' menu item
else if ( [menuItem tag] == 1002 ) {
if ( _mode == TCSearchMode ) {
[menuItem setTitle:@"Show Cheat Mode"];
}
else /* _mode == TCCheatMode */ {
[menuItem setTitle:@"Show Search Mode"];
}
}
// the 'edit variables' menu item
else if ( [menuItem tag] == 1003 ) {
return (_mode == TCCheatMode && [ibCheatVariableTable selectedRow] != -1);
}
// the 'clear search' menu item
else if ( [menuItem tag] == 1004 ) {
return [ibSearchClearButton isEnabled];
}
// the cancel menu item
else if ( [menuItem tag] == 1005 ) {
if ( !_cheater || _isCancelingTask ) {
return NO;
}
if ( _status == TCSearchingStatus ) {
[menuItem setTitle:@"Cancel Search"];
[menuItem setAction:@selector(ibCancelSearch:)];
}
else if ( _status == TCCheatingStatus ) {
[menuItem setTitle:@"Stop Cheat"];
[menuItem setAction:@selector(ibStopCheat:)];
}
else if ( _status == TCDumpingStatus ) {
[menuItem setTitle:@"Cancel Dump"];
[menuItem setAction:@selector(ibCancelDump:)];
}
else {
return NO;
}
}
return [super validateMenuItem:menuItem];
}
- (void)setDocumentChanged
{
// only count document changes if there are variables
// and if the pref is set
if ( [[NSUserDefaults standardUserDefaults] boolForKey:TCAskForSavePref] &&
([_cheatData variableCount] > 0 || [self isLoadedFromFile]) ) {
[self updateChangeCount:NSChangeDone];
}
}
- (int)numberOfRowsInTableView:(NSTableView *)aTableView
{
if ( aTableView == ibCheatVariableTable ) {
return [_cheatData variableCount];
}
else if ( aTableView == ibSearchVariableTable ) {
return [_searchData numberOfResults];
}
return 0;
}
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
{
NSString *identifier = [aTableColumn identifier];
if ( aTableView == ibCheatVariableTable ) {
Variable *variable = [_cheatData variableAtIndex:rowIndex];
if ( [identifier isEqualToString:@"enable"] ) {
return [NSNumber numberWithBool:[variable isEnabled]];
}
else if ( [identifier isEqualToString:@"variable"] ) {
return [variable typeString];
}
else if ( [identifier isEqualToString:@"address"] ) {
return [variable addressString];
}
else if ( [identifier isEqualToString:@"value"] ) {
return [variable stringValue];
}
}
else if ( aTableView == ibSearchVariableTable ) {
return [_searchData stringForRow:rowIndex];
}
return @"";
}
- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
{
NSString *identifier = [aTableColumn identifier];
if ( aTableView == ibCheatVariableTable ) {
Variable *variable = [_cheatData variableAtIndex:rowIndex];
if ( [identifier isEqualToString:@"address"] ) {
if ( [variable setAddressString:anObject] ) {
[self setDocumentChanged];
}
}
else if ( [identifier isEqualToString:@"value"] ) {
if ( [variable setStringValue:anObject] ) {
[self setDocumentChanged];
}
}
}
}
- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
{
NSTableView *aTableView = [aNotification object];
if ( aTableView == ibSearchVariableTable ) {
int selectedRows = [aTableView numberOfSelectedRows];
if ( selectedRows > 1 ) {
[ibSearchVariableButton setTitle:@"Add Variables"];
}
else if ( selectedRows == 1 ) {
[ibSearchVariableButton setTitle:@"Add Variable"];
}
}
}
- (BOOL)tableViewDidReceiveEnterKey:(NSTableView *)tableView
{
if ( tableView == ibSearchVariableTable ) {
[ibSearchVariableButton performClick:nil];
return YES;
}
return NO;
}
- (BOOL)tableViewDidReceiveSpaceKey:(NSTableView *)tableView
{
if ( tableView == ibCheatVariableTable ) {
[self ibSetVariableEnabled:nil];
return YES;
}
return NO;
}
- (NSString *)tableViewPasteboardType:(NSTableView *)tableView
{
return @"TCVariablePboardType";
}
- (NSData *)tableView:(NSTableView *)tableView copyRows:(NSArray *)rows
{
NSMutableArray *vars;
int i, top;
top = [rows count];
vars = [[NSMutableArray alloc] initWithCapacity:top];
// add the new variables
if ( tableView == ibSearchVariableTable ) {
for ( i = 0; i < top; i++ ) {
unsigned index = [[rows objectAtIndex:i] unsignedIntValue];
[vars addObject:[_searchData variableAtIndex:index]];
}
}
else {
for ( i = 0; i < top; i++ ) {
unsigned index = [[rows objectAtIndex:i] unsignedIntValue];
[vars addObject:[_cheatData variableAtIndex:index]];
}
}
return [NSArchiver archivedDataWithRootObject:[vars autorelease]];
}
- (void)tableView:(NSTableView *)tableView pasteRowsWithData:(NSData *)rowData
{
NSArray *vars = [NSUnarchiver unarchiveObjectWithData:rowData];
int i, top, lastRow;
if ( tableView == ibSearchVariableTable ) {
NSBeep();
return;
}
top = [vars count];
for ( i = 0; i < top; i++ ) {
Variable *var = [vars objectAtIndex:i];
[_cheatData addVariable:var];
}
lastRow = [_cheatData variableCount]-1;
[tableView reloadData];
if ( MacOSXVersion() >= 0x1030 ) {
[tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:lastRow] byExtendingSelection:NO];
}
else {
[tableView selectRow:lastRow byExtendingSelection:NO];
}
[tableView scrollRowToVisible:lastRow];
[self setDocumentChanged];
[self updateInterface];
}
- (void)tableView:(NSTableView *)tableView deleteRows:(NSArray *)rows
{
int i, len;
if ( tableView == ibCheatVariableTable ) {
len = [rows count];
for ( i = len-1; i >= 0; i-- ) {
[_cheatData removeVariableAtIndex:[[rows objectAtIndex:i] unsignedIntValue]];
}
// reselect the last item if the selection is now invalid
len = [_cheatData variableCount] - 1;
if ( [tableView selectedRow] > len ) {
if ( MacOSXVersion() >= 0x1030 ) {
[tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:len] byExtendingSelection:NO];
}
else {
[tableView selectRow:len byExtendingSelection:NO];
}
}
[tableView reloadData];
[self setDocumentChanged];
[self updateInterface];
}
}
// VariableTable Delegate
- (void)tableView:(NSTableView *)aTableView didChangeVisibleRows:(NSRange)rows
{
ChazLog( @"new visible rows: %@", NSStringFromRange( rows ) );
if ( [_searchData valuesLoaded] ) {
[self watchVariables];
}
}
// #############################################################################
#pragma mark Utility
// #############################################################################
+ (void)setGlobalTarget:(Process *)target
{
[target retain];
[_tc_target release];
_tc_target = target;
}
+ (Process *)globalTarget
{
return _tc_target;
}
- (void)showError:(NSString *)error
{
NSColor *red = [NSColor colorWithCalibratedRed:0.7 green:0.0 blue:0.0 alpha:1.0];
NSBeep();
[ibStatusText setDefaultStatus:[self defaultStatusString]];
[ibStatusText setTemporaryStatus:[NSString stringWithFormat:@"Error: %@", error] color:red duration:7.0];
}
- (BOOL)shouldConnectWithServer:(NSMenuItem *)item
{
id serverObject;
if ( _resolvingService ) {
// don't connect if a service is being resolved
[ibServerPopup selectItemAtIndex:[ibServerPopup indexOfItemWithRepresentedObject:_resolvingService]];
return NO;
}
if ( [item respondsToSelector:@selector(representedObject)] ) {
serverObject = [item representedObject];
}
else {
serverObject = [NSNull null];
}
if ( [_serverObject isEqual:serverObject] ) {
// already connected, don't connect
return NO;
}
return YES;
}
- (void)selectConnectedCheater
{
int index = [ibServerPopup indexOfItemWithRepresentedObject:_serverObject];
if ( index != -1 ) {
[ibServerPopup selectItemAtIndex:index];
}
}
- (void)connectWithServer:(NSMenuItem *)item
{
id serverObject;
if ( [item respondsToSelector:@selector(representedObject)] ) {
serverObject = [item representedObject];
}
else {
serverObject = [NSNull null];
}
// save a reference to the server object
[serverObject retain];
[_serverObject release];
_serverObject = serverObject;
}
- (void)disconnectFromCheater
{
NSMenu *blankMenu;
// don't do anything if we are already disconnected
if ( !_cheater ) {
return;
}
_status = TCIdleStatus;
// clear the search
[_searchData clearResults];
//[ibSearchVariableTable reloadData]; // this can cause a crash, so commenting it out for now.
// clear the selected process
[_process release];
_process = nil;
[_serverObject release];
_serverObject = nil;
if ( ![self isLoadedFromFile] ) {
[_cheatData setProcess:nil];
}
// clear the process menu
blankMenu = [[NSMenu alloc] initWithTitle:@""];
[blankMenu addItemWithTitle:@"" action:NULL keyEquivalent:@""];
[ibProcessPopup setMenu:blankMenu];
[blankMenu release];
// kill the connection
[_cheater disconnect];
[_cheater release];
_cheater = nil;
}
- (void)setConnectOnOpen:(BOOL)flag
{
_connectsOnOpen = flag;
}
- (void)connectWithURL:(NSString *)url
{
NSMenu *serverMenu = [ibServerPopup menu];
NSURL *theUrl = [NSURL URLWithString:url];
NSString *host;
int port;
NSData *addrData;
int indexIfAlreadyExists;
host = [theUrl host];
port = [[theUrl port] intValue];
if ( !host ) {
NSBeginInformationalAlertSheet( @"The Cheat can't parse the URL.", @"OK", nil, nil, ibWindow, self, NULL, NULL, NULL,
@"The Cheat can't connect to the server because \"%@\" is not a valid URL.", url );
goto FAILURE;
}
// use default port number
if ( !port ) {
port = TCDefaultListenPort;
}
addrData = [MySocket addressWithHost:host port:port];
if ( !addrData ) {
NSBeginInformationalAlertSheet( @"The Cheat can't find the server.", @"OK", nil, nil, ibWindow, self, NULL, NULL, NULL,
@"The Cheat can't connect to the server \"%@\" because it can't be found.", host );
goto FAILURE;
}
indexIfAlreadyExists = [serverMenu indexOfItemWithRepresentedObject:addrData];
if ( indexIfAlreadyExists == -1 ) {
NSMenuItem *menuItem;
// add the newly found service to the server popup
menuItem = [[NSMenuItem alloc] init];
[menuItem setTarget:self];
[menuItem setAction:@selector(ibSetCustomCheater:)];
[menuItem setTitle:[NSString stringWithFormat:@"%@:%i", host, port]];
[menuItem setRepresentedObject:addrData];
[self addServer:menuItem];
// select new item
[self ibSetCustomCheater:menuItem];
// cleanup
[menuItem release];
}
else {
// select matching item
[self ibSetCustomCheater:[serverMenu itemAtIndex:indexIfAlreadyExists]];
}
FAILURE:;
[self selectConnectedCheater];
}
- (void)watchVariables
{
NSRange range;
if ( [[NSUserDefaults standardUserDefaults] boolForKey:TCDisplayValuesPref] ) {
float interval = [[NSUserDefaults standardUserDefaults] floatForKey:TCValueUpdatePref];
range = [ibSearchVariableTable visibleRows];
[_cheater watchVariablesAtIndex:range.location count:range.length interval:interval];
}
else {
[_cheater stopWatchingVariables];
}
}
// #############################################################################
#pragma mark Notifications
// #############################################################################
- (void)_displayValuesPrefChanged:(NSNotification *)note
{
[self watchVariables];
}
- (void)_windowOnTopPrefChanged:(NSNotification *)note
{
ChazLog( @"_windowOnTopPrefChanged" );
if ( [[NSUserDefaults standardUserDefaults] boolForKey:TCWindowsOnTopPref] ) {
[ibWindow setLevel:NSPopUpMenuWindowLevel];
}
else {
[ibWindow setLevel:NSNormalWindowLevel];
}
}
- (void)_hitsDisplayedPrefChanged:(NSNotification *)note
{
// send preferences to the cheater
[_cheater limitReturnedResults:[[NSUserDefaults standardUserDefaults] integerForKey:TCHitsDisplayedPref]];
}
@end
Jump to Line
Something went wrong with that request. Please try again.