/*****************************************************************************\ Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. This file is licensed under the Snes9x License. For further information, consult the LICENSE file in the root directory. \*****************************************************************************/ /*********************************************************************************** SNES9X for Mac OS (c) Copyright John Stiles Snes9x for Mac OS X (c) Copyright 2001 - 2011 zones (c) Copyright 2002 - 2005 107 (c) Copyright 2002 PB1400c (c) Copyright 2004 Alexander and Sander (c) Copyright 2004 - 2005 Steven Seeger (c) Copyright 2005 Ryan Vogt ***********************************************************************************/ #include "snes9x.h" #include "memmap.h" #include "cheats.h" #include "mac-prefix.h" #include "mac-audio.h" #include "mac-dialog.h" #include "mac-os.h" #include "mac-screenshot.h" #include "mac-stringtools.h" #include "mac-cheatfinder.h" #define kCFNumBytesPop 'Size' #define kCFViewModeRad 'Mode' #define kCFCompModePop 'Math' #define kCFCompStoredRad 'RSto' #define kCFCompLastRad 'RLst' #define kCFCompThisRad 'RThs' #define kCFCompValueTxt 'CTxt' #define kCFSearchBtn 'BSea' #define kCFStoreValueBtn 'BSto' #define kCFWatchBtn 'BWat' #define kCFDrawerBtn 'Drwr' #define kCFWatchAddrTxt 'WTxt' #define kCFRestoreBtn 'BRes' #define kCFRemoveBtn 'BRem' #define kCFAddEntryBtn 'BAdd' #define kCFUserPane 'Pane' #define kCFSheetAddrTxt 'AEad' #define kCFSheetCurrentValueTxt 'AEcv' #define kCFSheetCheetValueTxt 'AEtx' #define kCFSheetDescriptionTxt 'AEde' #define kCFSheetAddBtn 'SHTa' #define kCFSheetCancelBtn 'SHTc' #define kCFListView 'List' #define kCFUpperViews 'UI_T' #define kCFLowerViews 'UI_B' #define kEventScrollableScrollThere 'ESST' #define kEventCheatFinderList 'ECFL' #define kControlListLinePart 172 #define MAIN_MEMORY_SIZE 0x20000 #define kCheatFinderListViewClassID CFSTR("com.snes9x.macos.snes9x.cheatfinder") enum { kCFHexadecimal = 1, kCFSignedDecimal, kCFUnsignedDecimal }; enum { kCFCompWithStored = 1, kCFCompWithLast, kCFCompWithThis }; enum { kCFSearchEqual = 1, kCFSearchNotEqual, kCFSearchGreater, kCFSearchGreaterOrEqual, kCFSearchLess, kCFSearchLessOrEqual }; typedef struct { IBNibRef nibRef; WindowRef main; WindowRef sheet; WindowRef drawer; HIViewRef list; HIViewRef scroll; EventHandlerRef sEref; EventHandlerUPP sUPP; } WindowData; typedef struct { HIViewRef view; HIPoint originPoint; HISize lineSize; Boolean inFocus; } ListViewData; Boolean cfIsWatching = false; extern SCheatData Cheat; static UInt8 *cfStoredRAM; static UInt8 *cfLastRAM; static UInt8 *cfCurrentRAM; static UInt8 *cfStatusFlag; static UInt32 *cfAddress; static SInt32 cfNumRows; static SInt32 cfListSelection; static SInt32 cfViewMode; static SInt32 cfCompMode; static SInt32 cfCompWith; static UInt32 cfViewNumBytes; static UInt32 cfWatchAddr; static Boolean cfIsNewGame; static Boolean cfIsStored; static Boolean cfDrawerShow; static int cfListAddrColumnWidth; static char cfWatchTextFormat[32]; static CTFontRef cfListLineCTFontRef; #ifdef MAC_TIGER_PANTHER_SUPPORT static ATSUStyle cfListLineATSUStyle; #endif static HIViewID kCheatFinderListViewID = { 'CHET', 'FNDR' }; static void CheatFinderSearch (WindowData *); static void CheatFinderRestoreList (WindowData *); static void CheatFinderRemoveFromList (WindowData *); static void CheatFinderAdjustButtons (WindowData *); static void CheatFinderBuildResultList (void); static void CheatFinderHandleAddEntryButton (WindowData *); static void CheatFinderMakeValueFormat (char *); static void CheatFinderAddEntry (SInt64, char *); static void CheatFinderBeginAddEntrySheet (WindowData *); static void CheatFinderEndAddEntrySheet (WindowData *); static void CheatFinderListViewScrollToThere (float, ListViewData *); static void CheatFinderListViewDraw (CGContextRef, HIRect *, ListViewData *); static float CheatFinderListViewSanityCheck (float, ListViewData *); static SInt64 CheatFinderReadBytes (UInt8 *, UInt32); static SInt64 CheatFinderGetValueEditText (ControlRef); static Boolean CheatFinderCompare (SInt64, SInt64); static HIViewPartCode CheatFinderListViewFindPart (EventRef, ListViewData *, SInt32 *); static pascal OSStatus CheatFinderListViewHandler (EventHandlerCallRef, EventRef, void *); static pascal OSStatus CheatFinderSheetEventHandler (EventHandlerCallRef, EventRef, void *); static pascal OSStatus CheatFinderWindowEventHandler (EventHandlerCallRef, EventRef, void *); static pascal OSStatus CheatFinderListFrameEventHandler (EventHandlerCallRef, EventRef, void *); void InitCheatFinder (void) { cfStoredRAM = new UInt8 [MAIN_MEMORY_SIZE + 10]; cfLastRAM = new UInt8 [MAIN_MEMORY_SIZE + 10]; cfCurrentRAM = new UInt8 [MAIN_MEMORY_SIZE + 10]; cfStatusFlag = new UInt8 [MAIN_MEMORY_SIZE + 10]; cfAddress = new UInt32[MAIN_MEMORY_SIZE + 10]; if (!cfStoredRAM || !cfLastRAM || !cfCurrentRAM || !cfStatusFlag || !cfAddress) QuitWithFatalError(0, "cheatfinder 01"); memset(cfCurrentRAM, 0x00, MAIN_MEMORY_SIZE + 10); cfViewMode = kCFUnsignedDecimal; cfViewNumBytes = 2; cfCompMode = kCFSearchEqual; cfCompWith = kCFCompWithThis; if (systemVersion >= 0x1050) { cfListLineCTFontRef = CTFontCreateWithName(CFSTR("Lucida Sans Typewriter Regular"), 11.0f, NULL); if (cfListLineCTFontRef == NULL) { cfListLineCTFontRef = CTFontCreateWithName(CFSTR("Menlo"), 11.0f, NULL); if (cfListLineCTFontRef == NULL) { cfListLineCTFontRef = CTFontCreateWithName(CFSTR("Monaco"), 11.0f, NULL); if (cfListLineCTFontRef == NULL) QuitWithFatalError(0, "cheatfinder 02"); } } } #ifdef MAC_TIGER_PANTHER_SUPPORT else { OSStatus err; err = CreateATSUIStyleFromFontFullNameAndSize("Lucida Sans Typewriter Regular", 11, &cfListLineATSUStyle); if (err) { err = CreateATSUIStyleFromFontFullNameAndSize("Menlo", 11, &cfListLineATSUStyle); if (err) { err = CreateATSUIStyleFromFontFullNameAndSize("Monaco", 11, &cfListLineATSUStyle); if (err) QuitWithFatalError(0, "cheatfinder 02"); } } } #endif } void ResetCheatFinder (void) { memset(cfStoredRAM, 0x00, MAIN_MEMORY_SIZE); memset(cfLastRAM, 0x00, MAIN_MEMORY_SIZE); memset(cfStatusFlag, 0xFF, MAIN_MEMORY_SIZE); cfWatchAddr = 0; cfIsNewGame = true; cfIsWatching = false; cfIsStored = false; cfDrawerShow = false; CheatFinderMakeValueFormat(cfWatchTextFormat); } void DeinitCheatFinder (void) { if (systemVersion >= 0x1050) CFRelease(cfListLineCTFontRef); #ifdef MAC_TIGER_PANTHER_SUPPORT else ATSUDisposeStyle(cfListLineATSUStyle); #endif delete [] cfStoredRAM; delete [] cfLastRAM; delete [] cfCurrentRAM; delete [] cfStatusFlag; delete [] cfAddress; } void CheatFinder (void) { static HIObjectClassRef cfListViewClass = NULL; OSStatus err; HIViewRef ctl; HIViewID cid; char num[256]; WindowData cf; EventHandlerRef wEref, pEref; EventHandlerUPP wUPP, pUPP; EventTypeSpec wEvents[] = { { kEventClassCommand, kEventCommandProcess }, { kEventClassCommand, kEventCommandUpdateStatus }, { kEventClassWindow, kEventWindowClose } }, pEvents[] = { { kEventClassControl, kEventControlDraw } }, cEvents[] = { { kEventClassHIObject, kEventHIObjectConstruct }, { kEventClassHIObject, kEventHIObjectInitialize }, { kEventClassHIObject, kEventHIObjectDestruct }, { kEventClassScrollable, kEventScrollableGetInfo }, { kEventClassScrollable, kEventScrollableScrollTo }, { kEventCheatFinderList, kEventScrollableScrollThere }, { kEventClassControl, kEventControlHitTest }, { kEventClassControl, kEventControlTrack }, { kEventClassControl, kEventControlValueFieldChanged }, { kEventClassControl, kEventControlDraw } }; if (!cartOpen) return; err = CreateNibReference(kMacS9XCFString, &(cf.nibRef)); if (err == noErr) { err = CreateWindowFromNib(cf.nibRef, CFSTR("CheatFinder"), &(cf.main)); if (err == noErr) { err = CreateWindowFromNib(cf.nibRef, CFSTR("CFDrawer"), &(cf.drawer)); if (err == noErr) { memcpy(cfCurrentRAM, Memory.RAM, MAIN_MEMORY_SIZE); CheatFinderBuildResultList(); err = noErr; if (!cfListViewClass) err = HIObjectRegisterSubclass(kCheatFinderListViewClassID, kHIViewClassID, 0, CheatFinderListViewHandler, GetEventTypeCount(cEvents), cEvents, NULL, &cfListViewClass); if (err == noErr) { HIObjectRef hiObject; HIViewRef userpane, scrollview, listview, imageview, root; HILayoutInfo layoutinfo; HIRect frame; HISize minSize; CGImageRef image; Rect rct; float pich; GetWindowBounds(cf.main, kWindowContentRgn, &rct); minSize.width = (float) (rct.right - rct.left); minSize.height = (float) (rct.bottom - rct.top ); err = SetWindowResizeLimits(cf.main, &minSize, NULL); root = HIViewGetRoot(cf.main); cid.id = 0; cid.signature = kCFUserPane; HIViewFindByID(root, cid, &userpane); err = HIScrollViewCreate(kHIScrollViewOptionsVertScroll, &scrollview); HIViewAddSubview(userpane, scrollview); HIViewGetBounds(userpane, &frame); cfListAddrColumnWidth = (int) (frame.size.width * 0.4); frame.origin.y += 16.0f; frame.size.height -= 16.0f; frame = CGRectInset(frame, 1.0f, 1.0f); HIViewSetFrame(scrollview, &frame); HIViewSetVisible(scrollview, true); cf.scroll = scrollview; layoutinfo.version = kHILayoutInfoVersionZero; HIViewGetLayoutInfo(scrollview, &layoutinfo); layoutinfo.binding.top.toView = userpane; layoutinfo.binding.top.kind = kHILayoutBindTop; layoutinfo.binding.bottom.toView = userpane; layoutinfo.binding.bottom.kind = kHILayoutBindBottom; layoutinfo.binding.left.toView = userpane; layoutinfo.binding.left.kind = kHILayoutBindLeft; layoutinfo.binding.right.toView = userpane; layoutinfo.binding.right.kind = kHILayoutBindRight; HIViewSetLayoutInfo(scrollview, &layoutinfo); err = HIObjectCreate(kCheatFinderListViewClassID, NULL, &hiObject); listview = (HIViewRef) hiObject; HIViewAddSubview(scrollview, listview); SetControl32BitMinimum(listview, 1); SetControl32BitMaximum(listview, cfNumRows); SetControl32BitValue(listview, 1); HIViewSetVisible(listview, true); cf.list = listview; cid.signature = kCFNumBytesPop; HIViewFindByID(root, cid, &ctl); SetControl32BitValue(ctl, cfViewNumBytes); cid.signature = kCFViewModeRad; HIViewFindByID(root, cid, &ctl); SetControl32BitValue(ctl, cfViewMode); cid.signature = kCFCompModePop; HIViewFindByID(root, cid, &ctl); SetControl32BitValue(ctl, cfCompMode); if (cfIsNewGame || (!cfIsStored && (cfCompWith == kCFCompWithStored))) cfCompWith = kCFCompWithThis; cid.signature = kCFCompStoredRad; HIViewFindByID(root, cid, &ctl); SetControl32BitValue(ctl, cfCompWith == kCFCompWithStored); if (cfIsStored) ActivateControl(ctl); else DeactivateControl(ctl); cid.signature = kCFCompLastRad; HIViewFindByID(root, cid, &ctl); SetControl32BitValue(ctl, cfCompWith == kCFCompWithLast); if (!cfIsNewGame) ActivateControl(ctl); else DeactivateControl(ctl); cid.signature = kCFCompThisRad; HIViewFindByID(root, cid, &ctl); SetControl32BitValue(ctl, cfCompWith == kCFCompWithThis); cid.signature = kCFCompValueTxt; HIViewFindByID(root, cid, &ctl); SetEditTextCFString(ctl, CFSTR(""), false); err = SetKeyboardFocus(cf.main, ctl, kControlFocusNextPart); cid.signature = kCFWatchBtn; HIViewFindByID(root, cid, &ctl); SetControl32BitValue(ctl, cfIsWatching); cid.signature = kCFDrawerBtn; HIViewFindByID(root, cid, &ctl); SetControl32BitValue(ctl, cfDrawerShow); cid.signature = kCFWatchAddrTxt; HIViewFindByID(root, cid, &ctl); if (cfIsWatching) { sprintf(num, "%06lX", cfWatchAddr + 0x7E0000); SetStaticTextCStr(ctl, num, false); } else SetStaticTextCFString(ctl, CFSTR(""), false); CheatFinderAdjustButtons(&cf); pUPP = NewEventHandlerUPP(CheatFinderListFrameEventHandler); err = InstallControlEventHandler(userpane, pUPP, GetEventTypeCount(pEvents), pEvents, (void *) userpane, &pEref); wUPP = NewEventHandlerUPP(CheatFinderWindowEventHandler); err = InstallWindowEventHandler (cf.main, wUPP, GetEventTypeCount(wEvents), wEvents, (void *) &cf, &wEref); pich = (float) (IPPU.RenderedScreenHeight >> ((IPPU.RenderedScreenHeight > 256) ? 1 : 0)); err = SetDrawerParent(cf.drawer, cf.main); err = SetDrawerOffsets(cf.drawer, 0.0f, (float) ((rct.bottom - rct.top) - (pich + 37))); image = CreateGameScreenCGImage(); if (image) { err = HIImageViewCreate(image, &imageview); if (err == noErr) { HIViewFindByID(HIViewGetRoot(cf.drawer), kHIViewWindowContentID, &ctl); HIViewAddSubview(ctl, imageview); HIImageViewSetOpaque(imageview, false); HIImageViewSetScaleToFit(imageview, true); HIViewSetVisible(imageview, true); frame.origin.x = 8.0f; frame.origin.y = 8.0f; frame.size.width = (float) SNES_WIDTH; frame.size.height = pich; HIViewSetFrame(imageview, &frame); } } MoveWindowPosition(cf.main, kWindowCheatFinder, true); ShowWindow(cf.main); if (cfDrawerShow) err = OpenDrawer(cf.drawer, kWindowEdgeDefault, false); err = RunAppModalLoopForWindow(cf.main); HideWindow(cf.main); SaveWindowPosition(cf.main, kWindowCheatFinder); err = RemoveEventHandler(pEref); DisposeEventHandlerUPP(pUPP); err = RemoveEventHandler(wEref); DisposeEventHandlerUPP(wUPP); if (image) CGImageRelease(image); } CFRelease(cf.drawer); } CFRelease(cf.main); } DisposeNibReference(cf.nibRef); memcpy(cfLastRAM, Memory.RAM, MAIN_MEMORY_SIZE); cfIsNewGame = false; } } static SInt64 CheatFinderReadBytes (UInt8 *mem, UInt32 addr) { switch (cfViewMode) { case kCFSignedDecimal: { switch (cfViewNumBytes) { case 1: return ((SInt64) (SInt8) mem[addr]); case 2: return ((SInt64) (SInt16) (mem[addr] | (mem[addr + 1] << 8))); case 4: return ((SInt64) (SInt32) (mem[addr] | (mem[addr + 1] << 8) | (mem[addr + 2] << 16) | (mem[addr + 3] << 24))); case 3: return ((SInt64) (((SInt32) ((mem[addr] | (mem[addr + 1] << 8) | (mem[addr + 2] << 16)) << 8)) >> 8)); } break; } case kCFUnsignedDecimal: case kCFHexadecimal: { switch (cfViewNumBytes) { case 1: return ((SInt64) (UInt8) mem[addr]); case 2: return ((SInt64) (UInt16) (mem[addr] | (mem[addr + 1] << 8))); case 3: return ((SInt64) (UInt32) (mem[addr] | (mem[addr + 1] << 8) | (mem[addr + 2] << 16))); case 4: return ((SInt64) (UInt32) (mem[addr] | (mem[addr + 1] << 8) | (mem[addr + 2] << 16) | (mem[addr + 3] << 24))); } break; } } return (0); } static pascal OSStatus CheatFinderWindowEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) { OSStatus err, result = eventNotHandledErr; WindowData *cf; HIViewRef control, ctl, root; HIViewID controlID, cid; Boolean update = false; char num[256]; cf = (WindowData *) inUserData; switch (GetEventClass(inEvent)) { case kEventClassWindow: { switch (GetEventKind(inEvent)) { case kEventWindowClose: { QuitAppModalLoopForWindow(cf->main); result = noErr; break; } } break; } case kEventClassCommand: { switch (GetEventKind(inEvent)) { HICommand tHICommand; case kEventCommandUpdateStatus: { err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand); if (err == noErr && tHICommand.commandID == 'clos') { UpdateMenuCommandStatus(true); result = noErr; } break; } case kEventCommandProcess: { err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand); if (err == noErr) { root = HIViewGetRoot(cf->main); controlID.id = 0; controlID.signature = tHICommand.commandID; HIViewFindByID(root, controlID, &control); switch (tHICommand.commandID) { case kCFNumBytesPop: { cfViewNumBytes = GetControl32BitValue(control); update = true; result = noErr; break; } case kCFViewModeRad: { cfViewMode = GetControl32BitValue(control); update = true; result = noErr; break; } case kCFCompModePop: { cfCompMode = GetControl32BitValue(control); result = noErr; break; } case kCFCompStoredRad: case kCFCompLastRad: case kCFCompThisRad: { if (tHICommand.commandID == kCFCompStoredRad) cfCompWith = kCFCompWithStored; else if (tHICommand.commandID == kCFCompLastRad) cfCompWith = kCFCompWithLast; else cfCompWith = kCFCompWithThis; cid.id = 0; cid.signature = kCFCompStoredRad; HIViewFindByID(root, cid, &ctl); SetControl32BitValue(ctl, cfCompWith == kCFCompWithStored); cid.signature = kCFCompLastRad; HIViewFindByID(root, cid, &ctl); SetControl32BitValue(ctl, cfCompWith == kCFCompWithLast); cid.signature = kCFCompThisRad; HIViewFindByID(root, cid, &ctl); SetControl32BitValue(ctl, cfCompWith == kCFCompWithThis); result = noErr; break; } case kCFStoreValueBtn: { memcpy(cfStoredRAM, Memory.RAM, MAIN_MEMORY_SIZE); cfIsStored = true; cid.id = 0; cid.signature = kCFCompStoredRad; HIViewFindByID(root, cid, &ctl); ActivateControl(ctl); result = noErr; break; } case kCFSearchBtn: { CheatFinderSearch(cf); update = true; result = noErr; break; } case kCFRemoveBtn: { CheatFinderRemoveFromList(cf); update = true; result = noErr; break; } case kCFRestoreBtn: { CheatFinderRestoreList(cf); update = true; result = noErr; break; } case kCFAddEntryBtn: { CheatFinderHandleAddEntryButton(cf); result = noErr; break; } case kCFDrawerBtn: { cfDrawerShow = !cfDrawerShow; if (cfDrawerShow) err = OpenDrawer(cf->drawer, kWindowEdgeDefault, false); else err = CloseDrawer(cf->drawer, false); result = noErr; break; } case kCFWatchBtn: { cid.id = 0; cid.signature = kCFWatchAddrTxt; HIViewFindByID(root, cid, &ctl); if (!cfIsWatching) { if (cfAddress[cfListSelection] > (0x20000 - cfViewNumBytes)) { PlayAlertSound(); SetControl32BitValue(control, 0); } else { cfWatchAddr = cfAddress[cfListSelection]; cfIsWatching = true; sprintf(num, "%06lX", cfWatchAddr + 0x7E0000); SetStaticTextCStr(ctl, num, true); } } else { cfWatchAddr = 0; cfIsWatching = false; SetControl32BitValue(control, 0); SetStaticTextCFString(ctl, CFSTR(""), true); CheatFinderAdjustButtons(cf); } result = noErr; break; } } if (update) { HIViewSetNeedsDisplay(cf->list, true); CheatFinderAdjustButtons(cf); } CheatFinderMakeValueFormat(cfWatchTextFormat); } break; } } break; } } return (result); } static SInt64 CheatFinderGetValueEditText (HIViewRef control) { SInt64 result = 0; UInt32 uvalue; SInt32 svalue; char num[256]; GetEditTextCStr(control, num); if (num[0] == 0) { SetEditTextCFString(control, CFSTR("0"), true); return (0); } switch (cfViewMode) { case kCFSignedDecimal: { if (sscanf(num, "%ld", &svalue) == 1) { switch (cfViewNumBytes) { case 1: { if (svalue > 127) { svalue = 127; SetEditTextCFString(control, CFSTR("127"), true); } else if (svalue < -128) { svalue = -128; SetEditTextCFString(control, CFSTR("-128"), true); } break; } case 2: { if (svalue > 32767) { svalue = 32767; SetEditTextCFString(control, CFSTR("32767"), true); } else if (svalue < -32768) { svalue = -32768; SetEditTextCFString(control, CFSTR("-32768"), true); } break; } case 3: { if (svalue > 8388607) { svalue = 8388607; SetEditTextCFString(control, CFSTR("8388607"), true); } else if (svalue < -8388608) { svalue = -8388608; SetEditTextCFString(control, CFSTR("-8388608"), true); } break; } } } else { svalue = 0; SetEditTextCFString(control, CFSTR("0"), true); } result = (SInt64) svalue; break; } case kCFUnsignedDecimal: { if (sscanf(num, "%lu", &uvalue) == 1) { switch (cfViewNumBytes) { case 1: { if (uvalue > 255) { uvalue = 255; SetEditTextCFString(control, CFSTR("255"), true); } break; } case 2: { if (uvalue > 65535) { uvalue = 65535; SetEditTextCFString(control, CFSTR("65535"), true); } break; } case 3: { if (uvalue > 16777215) { uvalue = 16777215; SetEditTextCFString(control, CFSTR("16777215"), true); } break; } } } else { uvalue = 0; SetEditTextCFString(control, CFSTR("0"), true); } result = (SInt64) uvalue; break; } case kCFHexadecimal: { if (sscanf(num, "%lx", &uvalue) == 1) { switch (cfViewNumBytes) { case 1: { if (uvalue > 0xFF) { uvalue = 0xFF; SetEditTextCFString(control, CFSTR("FF"), true); } break; } case 2: { if (uvalue > 0xFFFF) { uvalue = 0xFFFF; SetEditTextCFString(control, CFSTR("FFFF"), true); } break; } case 3: { if (uvalue > 0xFFFFFF) { uvalue = 0xFFFFFF; SetEditTextCFString(control, CFSTR("FFFFFF"), true); } break; } } } else { uvalue = 0; SetEditTextCFString(control, CFSTR("0"), true); } result = (SInt64) uvalue; break; } } return (result); } static void CheatFinderSearch (WindowData *cf) { SInt64 cmpvalue; UInt8 *mem; if (cfCompWith == kCFCompWithThis) { HIViewRef ctl; HIViewID cid = { kCFCompValueTxt, 0 }; HIViewFindByID(HIViewGetRoot(cf->main), cid, &ctl); cmpvalue = CheatFinderGetValueEditText(ctl); for (int i = 0; i < cfNumRows; i++) if (!CheatFinderCompare(CheatFinderReadBytes(cfCurrentRAM, cfAddress[i]), cmpvalue)) cfStatusFlag[cfAddress[i]] = 0; } else { mem = (cfCompWith == kCFCompWithStored) ? cfStoredRAM : cfLastRAM; for (int i = 0; i < cfNumRows; i++) if (!CheatFinderCompare(CheatFinderReadBytes(cfCurrentRAM, cfAddress[i]), CheatFinderReadBytes(mem, cfAddress[i]))) cfStatusFlag[cfAddress[i]] = 0; } CheatFinderBuildResultList(); SetControl32BitMaximum(cf->list, cfNumRows); SetControl32BitValue(cf->list, 1); } static Boolean CheatFinderCompare (SInt64 ramvalue, SInt64 cmpvalue) { switch (cfCompMode) { case kCFSearchEqual: return (ramvalue == cmpvalue); case kCFSearchNotEqual: return (ramvalue != cmpvalue); case kCFSearchGreater: return (ramvalue > cmpvalue); case kCFSearchGreaterOrEqual: return (ramvalue >= cmpvalue); case kCFSearchLess: return (ramvalue < cmpvalue); case kCFSearchLessOrEqual: return (ramvalue <= cmpvalue); } return (false); } static void CheatFinderBuildResultList (void) { cfNumRows = 0; for (int i = 0; i < MAIN_MEMORY_SIZE; i++) { if (cfStatusFlag[i] == 0xFF) { cfAddress[cfNumRows] = i; cfNumRows++; } } cfListSelection = 0; } static void CheatFinderAdjustButtons (WindowData *cf) { HIViewRef ctl, root; HIViewID cid; cid.id = 0; root = HIViewGetRoot(cf->main); if (cfNumRows > 0) { cid.signature = kCFAddEntryBtn; HIViewFindByID(root, cid, &ctl); ActivateControl(ctl); cid.signature = kCFRemoveBtn; HIViewFindByID(root, cid, &ctl); ActivateControl(ctl); cid.signature = kCFWatchBtn; HIViewFindByID(root, cid, &ctl); ActivateControl(ctl); } else { cid.signature = kCFAddEntryBtn; HIViewFindByID(root, cid, &ctl); DeactivateControl(ctl); cid.signature = kCFRemoveBtn; HIViewFindByID(root, cid, &ctl); DeactivateControl(ctl); if (!cfIsWatching) { cid.signature = kCFWatchBtn; HIViewFindByID(root, cid, &ctl); DeactivateControl(ctl); } } } static void CheatFinderRemoveFromList (WindowData *cf) { if (cfNumRows > 0) { cfStatusFlag[cfAddress[cfListSelection]] = 0; if (cfNumRows == 1) { cfNumRows = 0; SetControl32BitMaximum(cf->list, 0); SetControl32BitValue(cf->list, 1); } else { for (int i = cfListSelection; i < cfNumRows - 1; i++) cfAddress[i] = cfAddress[i + 1]; cfNumRows--; if (cfListSelection >= cfNumRows) cfListSelection = cfNumRows - 1; SetControl32BitMaximum(cf->list, cfNumRows); SetControl32BitValue(cf->list, cfListSelection + 1); } } } static void CheatFinderRestoreList (WindowData *cf) { memset(cfStatusFlag, 0xFF, MAIN_MEMORY_SIZE); CheatFinderBuildResultList(); SetControl32BitMaximum(cf->list, cfNumRows); SetControl32BitValue(cf->list, 1); } static void CheatFinderMakeValueFormat (char *text) { switch (cfViewMode) { case kCFSignedDecimal: case kCFUnsignedDecimal: { strcpy(text, "%lld"); break; } case kCFHexadecimal: { sprintf(text, "%%0%lullX", cfViewNumBytes * 2); break; } } } void CheatFinderDrawWatchAddr (void) { static char code[256]; uint16 *basePtr; int len; sprintf(code, cfWatchTextFormat, CheatFinderReadBytes(Memory.RAM, cfWatchAddr)); basePtr = GFX.Screen + 1; len = strlen(code); for (int i = 0; i < len; i++) { S9xDisplayChar(basePtr, code[i]); basePtr += (8 - 1); } } static void CheatFinderHandleAddEntryButton (WindowData *cf) { if (cfAddress[cfListSelection] > (0x20000 - cfViewNumBytes)) PlayAlertSound(); else if (Cheat.g.size() + cfViewNumBytes > MAC_MAX_CHEATS) AppearanceAlert(kAlertCautionAlert, kS9xMacAlertCFCantAddEntry, kS9xMacAlertCFCantAddEntryHint); else CheatFinderBeginAddEntrySheet(cf); } static void CheatFinderBeginAddEntrySheet (WindowData *cf) { OSStatus err; HIViewRef ctl, root; HIViewID cid; UInt32 addr; char str[256], form[256]; EventTypeSpec sEvents[] = { { kEventClassCommand, kEventCommandProcess }, { kEventClassCommand, kEventCommandUpdateStatus } }; err = CreateWindowFromNib(cf->nibRef, CFSTR("CFAddEntry"), &(cf->sheet)); if (err == noErr) { addr = cfAddress[cfListSelection]; root = HIViewGetRoot(cf->sheet); cid.id = 0; cid.signature = kCFSheetAddrTxt; HIViewFindByID(root, cid, &ctl); sprintf(str, "%06lX", addr + 0x7E0000); SetStaticTextCStr(ctl, str, false); cid.signature = kCFSheetCurrentValueTxt; HIViewFindByID(root, cid, &ctl); CheatFinderMakeValueFormat(form); sprintf(str, form, CheatFinderReadBytes(cfCurrentRAM, addr)); SetStaticTextCStr(ctl, str, false); cid.signature = kCFSheetCheetValueTxt; HIViewFindByID(root, cid, &ctl); SetEditTextCStr(ctl, str, false); err = ClearKeyboardFocus(cf->sheet); err = SetKeyboardFocus(cf->sheet, ctl, kControlFocusNextPart); cid.signature = kCFSheetDescriptionTxt; HIViewFindByID(root, cid, &ctl); sprintf(str, "%06lX-%06lX", addr + 0x7E0000, addr + cfViewNumBytes - 1 + 0x7E0000); SetStaticTextCStr(ctl, str, false); cf->sUPP = NewEventHandlerUPP(CheatFinderSheetEventHandler); err = InstallWindowEventHandler(cf->sheet, cf->sUPP, GetEventTypeCount(sEvents), sEvents, (void *) cf, &(cf->sEref)); err = ShowSheetWindow(cf->sheet, cf->main); } } static void CheatFinderEndAddEntrySheet (WindowData *cf) { if (cf->sheet) { OSStatus err; err = HideSheetWindow(cf->sheet); err = RemoveEventHandler(cf->sEref); DisposeEventHandlerUPP(cf->sUPP); CFRelease(cf->sheet); } } static pascal OSStatus CheatFinderSheetEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) { OSStatus err, result = eventNotHandledErr; WindowData *cf = (WindowData *) inUserData; switch (GetEventClass(inEvent)) { case kEventClassCommand: { switch (GetEventKind(inEvent)) { HICommand tHICommand; case kEventCommandUpdateStatus: { err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand); if (err == noErr && tHICommand.commandID == 'clos') { UpdateMenuCommandStatus(false); result = noErr; } break; } case kEventCommandProcess: { err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand); if (err == noErr) { switch (tHICommand.commandID) { case kCFSheetAddBtn: { HIViewRef ctl, root; HIViewID cid; CFStringRef str; SInt64 rv; char buf[256]; root = HIViewGetRoot(cf->sheet); cid.id = 0; cid.signature = kCFSheetCheetValueTxt; HIViewFindByID(root, cid, &ctl); rv = CheatFinderGetValueEditText(ctl); cid.signature = kCFSheetDescriptionTxt; HIViewFindByID(root, cid, &ctl); buf[0] = 0; CopyEditTextCFString(ctl, &str); if (str) { strcpy(buf, GetMultiByteCharacters(str, 19)); CFRelease(str); } if (buf[0] == 0) { buf[0] = ' '; buf[1] = 0; } CheatFinderAddEntry(rv, buf); CheatFinderEndAddEntrySheet(cf); result = noErr; break; } case kCFSheetCancelBtn: { CheatFinderEndAddEntrySheet(cf); result = noErr; break; } } } break; } } break; } } return (result); } static void CheatFinderAddEntry (SInt64 value, char *description) { UInt32 addr, v; addr = cfAddress[cfListSelection]; v = (UInt32) (SInt32) value; for (unsigned int i = 0; i < cfViewNumBytes; i++) { char code[10]; snprintf(code, 10, "%x=%x", addr + i + 0x7E0000, (UInt8) ((v & (0x000000FF << (i * 8))) >> (i * 8))); int index = S9xAddCheatGroup(description, code); if(index >= 0) S9xEnableCheatGroup(index); } } static pascal OSStatus CheatFinderListFrameEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *userData) { OSStatus err, result = eventNotHandledErr; HIViewRef view = (HIViewRef) userData; switch (GetEventClass(inEvent)) { case kEventClassControl: { switch (GetEventKind(inEvent)) { case kEventControlDraw: { CGContextRef ctx; HIRect bounds; HIViewGetBounds(view, &bounds); cfListAddrColumnWidth = (int) (bounds.size.width * 0.4); err = GetEventParameter(inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof(CGContextRef), NULL, &ctx); if (err == noErr) { HIRect drawBounds; if (systemVersion >= 0x1040) { HIShapeRef shape = NULL; err = GetEventParameter(inEvent, kEventParamShape, typeHIShapeRef, NULL, sizeof(HIShapeRef), NULL, &shape); if (err == noErr) { if (shape) HIShapeGetBounds(shape, &drawBounds); else drawBounds = bounds; } } #ifdef MAC_PANTHER_SUPPORT else { RgnHandle rgn = NULL; err = GetEventParameter(inEvent, kEventParamRgnHandle, typeQDRgnHandle, NULL, sizeof(RgnHandle), NULL, &rgn); if (err == noErr) { if (rgn) { Rect rgnBounds; GetRegionBounds(rgn, &rgnBounds); drawBounds.origin.x = (float) rgnBounds.left; drawBounds.origin.y = (float) rgnBounds.top; drawBounds.size.width = (float) (rgnBounds.right - rgnBounds.left); drawBounds.size.height = (float) (rgnBounds.bottom - rgnBounds.top ); } else drawBounds = bounds; } } #endif if (err == noErr) { if (CGRectContainsRect(drawBounds, bounds)) { HIRect rct; HIThemeFrameDrawInfo frameinfo; HIThemeButtonDrawInfo buttoninfo; HIThemeTextInfo textinfo; CFStringRef str; buttoninfo.version = 0; buttoninfo.state = kThemeStateActive; buttoninfo.kind = kThemeListHeaderButton; buttoninfo.value = kThemeButtonOff; buttoninfo.adornment = kThemeAdornmentNone; frameinfo.version = 0; frameinfo.state = kThemeStateActive; frameinfo.kind = kHIThemeFrameListBox; frameinfo.isFocused = false; textinfo.version = 0; textinfo.state = kThemeStateActive; textinfo.fontID = kThemeSmallSystemFont; textinfo.verticalFlushness = kHIThemeTextVerticalFlushCenter; textinfo.options = 0; textinfo.truncationPosition = kHIThemeTextTruncationNone; textinfo.truncationMaxLines = 0; textinfo.truncationHappened = false; CGContextSaveGState(ctx); rct = CGRectInset(bounds, 1.0f, 1.0f); err = HIThemeDrawFrame(&rct, &frameinfo, ctx, kHIThemeOrientationNormal); rct = bounds; rct.size.height = 16.0f; rct.size.width = (float) cfListAddrColumnWidth + 1.0f; err = HIThemeDrawButton(&rct, &buttoninfo, ctx, kHIThemeOrientationNormal, NULL); CGContextSetRGBFillColor(ctx, 0.0f, 0.0f, 0.0f, 1.0f); rct.size.width = (float) cfListAddrColumnWidth; rct.origin.y++; textinfo.horizontalFlushness = kHIThemeTextHorizontalFlushCenter; str = CFCopyLocalizedString(CFSTR("Address"), "address"); if (str) { err = HIThemeDrawTextBox(str, &rct, &textinfo, ctx, kHIThemeOrientationNormal); CFRelease(str); } rct = bounds; rct.size.height = 16.0f; rct.size.width -= (float) cfListAddrColumnWidth; rct.origin.x += (float) cfListAddrColumnWidth; err = HIThemeDrawButton(&rct, &buttoninfo, ctx, kHIThemeOrientationNormal, NULL); CGContextSetRGBFillColor(ctx, 0.0f, 0.0f, 0.0f, 1.0f); rct.size.width -= 28.0f; rct.origin.y++; textinfo.horizontalFlushness = kHIThemeTextHorizontalFlushRight; str = CFCopyLocalizedString(CFSTR("Value"), "value"); if (str) { err = HIThemeDrawTextBox(str, &rct, &textinfo, ctx, kHIThemeOrientationNormal); CFRelease(str); } rct = bounds; rct.size.height = 16.0f; rct.size.width = 16.0f; rct.origin.x = bounds.size.width - 16.0f; err = HIThemeDrawButton(&rct, &buttoninfo, ctx, kHIThemeOrientationNormal, NULL); CGContextRestoreGState(ctx); } result = noErr; } } break; } } break; } } return (result); } static void CheatFinderListViewDraw (CGContextRef ctx, HIRect *bounds, ListViewData *myData) { static Boolean init = true; if (systemVersion >= 0x1050) { static CGRect aRct, vRct; CTLineRef line; CFDictionaryRef attr; CFAttributedStringRef astr; CFStringRef str; HIRect lineBounds; SInt32 start, end, val, max; float ax, vx, y, f; char format[32], t1[64], t2[64]; CFStringRef keys[] = { kCTFontAttributeName, kCTForegroundColorAttributeName }; CFTypeRef bval[] = { cfListLineCTFontRef, CGColorGetConstantColor(kCGColorBlack) }, wval[] = { cfListLineCTFontRef, CGColorGetConstantColor(kCGColorWhite) }; CheatFinderMakeValueFormat(format); start = (SInt32) (myData->originPoint.y / myData->lineSize.height); end = (SInt32) ((myData->originPoint.y + bounds->size.height) / myData->lineSize.height) + 1; y = start * myData->lineSize.height - myData->originPoint.y; lineBounds = *bounds; lineBounds.size.height = myData->lineSize.height; lineBounds.origin.y = y; val = GetControl32BitValue(myData->view) - 1; max = GetControl32BitMaximum(myData->view); attr = CFDictionaryCreate(kCFAllocatorDefault, (const void **) &keys, (const void **) &bval, sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CGContextSetTextMatrix(ctx, CGAffineTransformIdentity); if (init) { CGContextSetTextPosition(ctx, 0.0f, 0.0f); astr = CFAttributedStringCreate(kCFAllocatorDefault, CFSTR("FFFFFF"), attr); line = CTLineCreateWithAttributedString(astr); aRct = CTLineGetImageBounds(line, ctx); CFRelease(line); CFRelease(astr); astr = CFAttributedStringCreate(kCFAllocatorDefault, CFSTR("FFFFFFFFFFF"), attr); line = CTLineCreateWithAttributedString(astr); vRct = CTLineGetImageBounds(line, ctx); CFRelease(line); CFRelease(astr); init = false; } ax = (float) (int) (((float) cfListAddrColumnWidth - 2.0 - aRct.size.width) / 2.0); vx = (float) (int) (lineBounds.origin.x + lineBounds.size.width - vRct.size.width - 12.0); for (int i = start; i <= end; i++) { if ((i == val) && cfNumRows) CGContextSetRGBFillColor(ctx, 59.0f / 256.0f, 124.0f / 256.0f, 212.0f / 256.0f, 1.0f); else if ((i - start) % 2 == 0) CGContextSetRGBFillColor(ctx, 256.0f / 256.0f, 256.0f / 256.0f, 256.0f / 256.0f, 1.0f); else CGContextSetRGBFillColor(ctx, 237.0f / 256.0f, 244.0f / 256.0f, 254.0f / 256.0f, 1.0f); CGContextFillRect(ctx, lineBounds); if (i < max) { CGContextScaleCTM(ctx, 1, -1); if (i == val) { CFRelease(attr); attr = CFDictionaryCreate(kCFAllocatorDefault, (const void **) &keys, (const void **) &wval, sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } f = -(y + 12.0f); sprintf(t1, "%06lX", cfAddress[i] + 0x7E0000); str = CFStringCreateWithCString(kCFAllocatorDefault, t1, kCFStringEncodingUTF8); astr = CFAttributedStringCreate(kCFAllocatorDefault, str, attr); line = CTLineCreateWithAttributedString(astr); CGContextSetTextPosition(ctx, ax, f); CTLineDraw(line, ctx); CFRelease(line); CFRelease(astr); CFRelease(str); sprintf(t2, format, CheatFinderReadBytes(cfCurrentRAM, cfAddress[i])); strcpy(t1, " "); t1[11 - strlen(t2)] = 0; strcat(t1, t2); str = CFStringCreateWithCString(kCFAllocatorDefault, t1, kCFStringEncodingUTF8); astr = CFAttributedStringCreate(kCFAllocatorDefault, str, attr); line = CTLineCreateWithAttributedString(astr); CGContextSetTextPosition(ctx, vx, f); CTLineDraw(line, ctx); CFRelease(line); CFRelease(astr); CFRelease(str); CGContextScaleCTM(ctx, 1, -1); if (i == val) { CFRelease(attr); attr = CFDictionaryCreate(kCFAllocatorDefault, (const void **) &keys, (const void **) &bval, sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } } y += myData->lineSize.height; lineBounds.origin.y += myData->lineSize.height; } CFRelease(attr); } #ifdef MAC_TIGER_PANTHER_SUPPORT else { static Rect aRect = { 0, 0, 0, 0 }, vRect = { 0, 0, 0, 0 }; OSStatus err; ATSUTextLayout layout; HIRect lineBounds; UniCharCount runLength[1], len; SInt32 start, end, val, max; Fixed ax, vx, f; float y; UniChar unistr[64]; char format[32], t1[64], t2[64]; ATSUAttributeTag theTags[] = { kATSUCGContextTag }; ByteCount theSizes[] = { sizeof(CGContextRef) }; ATSUAttributeValuePtr theValues[] = { &ctx }; CheatFinderMakeValueFormat(format); start = (SInt32) (myData->originPoint.y / myData->lineSize.height); end = (SInt32) ((myData->originPoint.y + bounds->size.height) / myData->lineSize.height) + 1; y = start * myData->lineSize.height - myData->originPoint.y; lineBounds = *bounds; lineBounds.size.height = myData->lineSize.height; lineBounds.origin.y = y; val = GetControl32BitValue(myData->view) - 1; max = GetControl32BitMaximum(myData->view); if (init) { f = Long2Fix(0); for (unsigned int n = 0; n < 11; n++) unistr[n] = 'F'; len = runLength[0] = 6; err = ATSUCreateTextLayoutWithTextPtr(unistr, kATSUFromTextBeginning, kATSUToTextEnd, len, 1, runLength, &cfListLineATSUStyle, &layout); err = ATSUSetLayoutControls(layout, sizeof(theTags) / sizeof(theTags[0]), theTags, theSizes, theValues); err = ATSUMeasureTextImage(layout, kATSUFromTextBeginning, kATSUToTextEnd, f, f, &aRect); err = ATSUDisposeTextLayout(layout); len = runLength[0] = 11; err = ATSUCreateTextLayoutWithTextPtr(unistr, kATSUFromTextBeginning, kATSUToTextEnd, len, 1, runLength, &cfListLineATSUStyle, &layout); err = ATSUSetLayoutControls(layout, sizeof(theTags) / sizeof(theTags[0]), theTags, theSizes, theValues); err = ATSUMeasureTextImage(layout, kATSUFromTextBeginning, kATSUToTextEnd, f, f, &vRect); err = ATSUDisposeTextLayout(layout); init = false; } ax = Long2Fix((cfListAddrColumnWidth - 2 - (aRect.right - aRect.left)) >> 1); vx = Long2Fix((int) (lineBounds.origin.x + lineBounds.size.width) - (vRect.right - vRect.left) - 13); for (int i = start; i <= end; i++) { if ((i == val) && cfNumRows) CGContextSetRGBFillColor(ctx, 59.0f / 256.0f, 124.0f / 256.0f, 212.0f / 256.0f, 1.0f); else if ((i - start) % 2 == 0) CGContextSetRGBFillColor(ctx, 256.0f / 256.0f, 256.0f / 256.0f, 256.0f / 256.0f, 1.0f); else CGContextSetRGBFillColor(ctx, 237.0f / 256.0f, 244.0f / 256.0f, 254.0f / 256.0f, 1.0f); CGContextFillRect(ctx, lineBounds); if (i < max) { CGContextScaleCTM(ctx, 1, -1); if (i == val) CGContextSetRGBFillColor(ctx, 1.0f, 1.0f, 1.0f, 1.0f); else CGContextSetRGBFillColor(ctx, 0.0f, 0.0f, 0.0f, 1.0f); f = Long2Fix(-((int) y + 12)); sprintf(t1, "%06lX", cfAddress[i] + 0x7E0000); len = runLength[0] = strlen(t1); for (unsigned int n = 0; n < len; n++) unistr[n] = t1[n]; err = ATSUCreateTextLayoutWithTextPtr(unistr, kATSUFromTextBeginning, kATSUToTextEnd, len, 1, runLength, &cfListLineATSUStyle, &layout); err = ATSUSetLayoutControls(layout, sizeof(theTags) / sizeof(theTags[0]), theTags, theSizes, theValues); err = ATSUDrawText(layout, kATSUFromTextBeginning, kATSUToTextEnd, ax, f); err = ATSUDisposeTextLayout(layout); sprintf(t2, format, CheatFinderReadBytes(cfCurrentRAM, cfAddress[i])); strcpy(t1, " "); t1[11 - strlen(t2)] = 0; strcat(t1, t2); len = runLength[0] = strlen(t1); for (unsigned int n = 0; n < len; n++) unistr[n] = t1[n]; err = ATSUCreateTextLayoutWithTextPtr(unistr, kATSUFromTextBeginning, kATSUToTextEnd, len, 1, runLength, &cfListLineATSUStyle, &layout); err = ATSUSetLayoutControls(layout, sizeof(theTags) / sizeof(theTags[0]), theTags, theSizes, theValues); err = ATSUDrawText(layout, kATSUFromTextBeginning, kATSUToTextEnd, vx, f); err = ATSUDisposeTextLayout(layout); CGContextScaleCTM(ctx, 1, -1); } y += myData->lineSize.height; lineBounds.origin.y += myData->lineSize.height; } } #endif } static HIViewPartCode CheatFinderListViewFindPart (EventRef inEvent, ListViewData *myData, SInt32 *whichLine) { OSStatus err; HIViewPartCode part; HIPoint hipt; SInt32 start, line; float y; part = kControlNoPart; start = (SInt32) (myData->originPoint.y / myData->lineSize.height); y = start * myData->lineSize.height - myData->originPoint.y; err = GetEventParameter(inEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(hipt), NULL, &hipt); if (err == noErr) { line = start + (SInt32) ((hipt.y - y - 1) / myData->lineSize.height) + 1; if (line <= GetControl32BitMaximum(myData->view)) part = kControlListLinePart; if (whichLine != NULL) *whichLine = line; } return (part); } static float CheatFinderListViewSanityCheck (float where, ListViewData *myData) { HIRect bounds; HISize imageSize; HIViewGetBounds(myData->view, &bounds); imageSize = myData->lineSize; imageSize.height *= GetControl32BitMaximum(myData->view); if (where + bounds.size.height > imageSize.height) where = imageSize.height - bounds.size.height; if (where < 0) where = 0; return (where); } static void CheatFinderListViewScrollToThere (float where, ListViewData *myData) { OSStatus err; EventRef theEvent; HIPoint whereP = { 0.0f, where }; err = CreateEvent(kCFAllocatorDefault, kEventCheatFinderList, kEventScrollableScrollThere, GetCurrentEventTime(), kEventAttributeUserEvent, &theEvent); if (err == noErr) { err = SetEventParameter(theEvent, kEventParamOrigin, typeHIPoint, sizeof(whereP), &whereP); if (err == noErr) err = SendEventToEventTarget(theEvent, GetControlEventTarget(myData->view)); ReleaseEvent(theEvent); } } static pascal OSStatus CheatFinderListViewHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) { OSStatus err, result = eventNotHandledErr; ListViewData *myData = (ListViewData *) inUserData; switch (GetEventClass(inEvent)) { case kEventClassHIObject: { switch (GetEventKind(inEvent)) { case kEventHIObjectConstruct: { ListViewData *data; data = (ListViewData *) calloc(1, sizeof(ListViewData)); if (data) { HIViewRef epView; err = GetEventParameter(inEvent, kEventParamHIObjectInstance, typeHIObjectRef, NULL, sizeof(epView), NULL, &epView); if (err == noErr) { data->view = epView; data->originPoint.x = 0.0f; data->originPoint.y = 0.0f; data->lineSize.width = 0.0f; data->lineSize.height = 16.0f; data->inFocus = false; SetControlID(epView, &kCheatFinderListViewID); result = SetEventParameter(inEvent, kEventParamHIObjectInstance, typeVoidPtr, sizeof(data), &data); } } break; } case kEventHIObjectDestruct: { if (myData) free(myData); result = noErr; break; } case kEventHIObjectInitialize: { result = CallNextEventHandler(inHandlerCallRef, inEvent); break; } } break; } case kEventClassScrollable: { switch (GetEventKind(inEvent)) { case kEventScrollableGetInfo: { HIRect bounds; HISize imageSize; HIViewGetBounds(myData->view, &bounds); imageSize = myData->lineSize; imageSize.height *= GetControl32BitMaximum(myData->view); err = SetEventParameter(inEvent, kEventParamImageSize, typeHISize, sizeof(imageSize), &imageSize); err = SetEventParameter(inEvent, kEventParamViewSize, typeHISize, sizeof(bounds.size), &bounds.size); err = SetEventParameter(inEvent, kEventParamLineSize, typeHISize, sizeof(myData->lineSize), &myData->lineSize); err = SetEventParameter(inEvent, kEventParamOrigin, typeHIPoint, sizeof(myData->originPoint), &myData->originPoint); result = noErr; break; } case kEventScrollableScrollTo: { HIPoint where; err = GetEventParameter(inEvent, kEventParamOrigin, typeHIPoint, NULL, sizeof(where), NULL, &where); if (err == noErr) { where.y = CheatFinderListViewSanityCheck(where.y, myData); if ((int) myData->originPoint.y == (int) where.y) break; myData->originPoint.y = where.y; HIViewSetNeedsDisplay(myData->view, true); } break; } } break; } case kEventCheatFinderList: { switch (GetEventKind(inEvent)) { case kEventScrollableScrollThere: { HIPoint where; err = GetEventParameter(inEvent, kEventParamOrigin, typeHIPoint, NULL, sizeof(where), NULL, &where); if (err == noErr) { where.y = CheatFinderListViewSanityCheck(where.y, myData); if ((int) myData->originPoint.y != (int) where.y) { myData->originPoint.y = where.y; SetControl32BitValue(HIViewGetNextView(myData->view), (SInt32) myData->originPoint.y); HIViewSetNeedsDisplay(myData->view, true); } } break; } } break; } case kEventClassControl: { switch (GetEventKind(inEvent)) { case kEventControlDraw: { CGContextRef ctx; err = GetEventParameter(inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof(ctx), NULL, &ctx); if (err == noErr) { HIRect bounds; HIViewGetBounds(myData->view, &bounds); CGContextSaveGState(ctx); CheatFinderListViewDraw(ctx, &bounds, myData); CGContextRestoreGState(ctx); result = noErr; } break; } case kEventControlHitTest: { HIViewPartCode part; part = CheatFinderListViewFindPart(inEvent, myData, NULL); result = SetEventParameter(inEvent, kEventParamControlPart, typeControlPartCode, sizeof(part), &part); break; } case kEventControlTrack: { MouseTrackingResult trackResult; WindowRef window; HIViewRef contentView; HIViewPartCode part; HIRect frame; SInt32 whichLine = 0; part = CheatFinderListViewFindPart(inEvent, myData, &whichLine); if (part == kControlListLinePart) SetControl32BitValue(myData->view, whichLine); window = GetControlOwner(myData->view); HIViewFindByID(HIViewGetRoot(window), kHIViewWindowContentID, &contentView); HIViewGetFrame(myData->view, &frame); HIViewConvertRect(&frame, myData->view, contentView); HIPoint hipt; #ifdef MAC_TIGER_PANTHER_SUPPORT CGrafPtr oldPort; Point qdpt; Boolean portChanged = false; if (systemVersion < 0x1050) portChanged = QDSwapPort(GetWindowPort(window), &oldPort); #endif if (systemVersion >= 0x1050) err = HIViewTrackMouseLocation(contentView, 0, kEventDurationForever, 0, NULL, &hipt, NULL, NULL, &trackResult); #ifdef MAC_TIGER_PANTHER_SUPPORT else TrackMouseLocation(NULL, &qdpt, &trackResult); #endif while (trackResult != kMouseTrackingMouseUp) { SInt32 start, line = 0; float y; start = (SInt32) (myData->originPoint.y / myData->lineSize.height); y = start * myData->lineSize.height - myData->originPoint.y; if (systemVersion >= 0x1050) { line = (SInt32) ((hipt.y - frame.origin.y - y - 1) / myData->lineSize.height) + 1; if (hipt.y < frame.origin.y) line--; } #ifdef MAC_TIGER_PANTHER_SUPPORT else { line = (SInt32) ((qdpt.v - frame.origin.y - y - 1) / myData->lineSize.height) + 1; if (qdpt.v < frame.origin.y) line--; } #endif line += start; if (line != whichLine) { whichLine = line; SetControl32BitValue(myData->view, whichLine); SInt32 goThere = -1; if (whichLine <= start) goThere = whichLine - 1; else if (whichLine >= start + (SInt32) (frame.size.height / myData->lineSize.height) + 1) goThere = whichLine - (SInt32) (frame.size.height / myData->lineSize.height) - 1; if (goThere != -1) { float where; where = goThere * myData->lineSize.height; where = CheatFinderListViewSanityCheck(where, myData); if ((int) where != (int) myData->originPoint.y) CheatFinderListViewScrollToThere(where, myData); } } if (systemVersion >= 0x1050) err = HIViewTrackMouseLocation(contentView, 0, kEventDurationForever, 0, NULL, &hipt, NULL, NULL, &trackResult); #ifdef MAC_TIGER_PANTHER_SUPPORT else TrackMouseLocation(NULL, &qdpt, &trackResult); #endif } #ifdef MAC_TIGER_PANTHER_SUPPORT if (systemVersion < 0x1050) { if (portChanged) QDSwapPort(oldPort, NULL); } #endif err = SetEventParameter(inEvent, kEventParamControlPart, typeControlPartCode, sizeof(part), &part); cfListSelection = GetControl32BitValue(myData->view) - 1; result = noErr; break; } case kEventControlValueFieldChanged: { EventRef theEvent; myData->originPoint.y = CheatFinderListViewSanityCheck(myData->originPoint.y, myData); err = CreateEvent(kCFAllocatorDefault, kEventClassScrollable, kEventScrollableInfoChanged, GetCurrentEventTime(), kEventAttributeUserEvent, &theEvent); if (err == noErr) { err = SendEventToEventTarget(theEvent, GetControlEventTarget(myData->view)); ReleaseEvent(theEvent); HIViewSetNeedsDisplay(myData->view, true); } break; } } break; } } return (result); }