1 /*****************************************************************************\
2 Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3 This file is licensed under the Snes9x License.
4 For further information, consult the LICENSE file in the root directory.
5 \*****************************************************************************/
6
7 /***********************************************************************************
8 SNES9X for Mac OS (c) Copyright John Stiles
9
10 Snes9x for Mac OS X
11
12 (c) Copyright 2001 - 2011 zones
13 (c) Copyright 2002 - 2005 107
14 (c) Copyright 2002 PB1400c
15 (c) Copyright 2004 Alexander and Sander
16 (c) Copyright 2004 - 2005 Steven Seeger
17 (c) Copyright 2005 Ryan Vogt
18 ***********************************************************************************/
19
20
21 #include "port.h"
22 #include "cheats.h"
23
24 #define __STDC_FORMAT_MACROS
25 #include <inttypes.h>
26
27 #include "mac-prefix.h"
28 #include "mac-dialog.h"
29 #include "mac-os.h"
30 #include "mac-stringtools.h"
31 #include "mac-cheat.h"
32
33 #define kDataBrowser 'BRSR'
34 #define kCmCheckBox 'CHK_'
35 #define kCmAddress 'ADDR'
36 #define kCmValue 'VALU'
37 #define kCmDescription 'DESC'
38 #define kNewButton 'NEW_'
39 #define kDelButton 'DEL_'
40 #define kAllButton 'ALL_'
41
42 extern SCheatData Cheat;
43
44 typedef struct
45 {
46 uint32 id;
47 uint32 address;
48 uint8 value;
49 bool8 valid;
50 bool8 enabled;
51 char description[22];
52 } CheatItem;
53
54 static WindowRef wRef;
55 static HIViewRef dbRef;
56 static CheatItem citem[MAC_MAX_CHEATS];
57 static uint32 numofcheats;
58
59 static void InitCheatItems (void);
60 static void ImportCheatItems (void);
61 static void DetachCheatItems (void);
62 static void AddCheatItem (void);
63 static void DeleteCheatItem (void);
64 static void EnableAllCheatItems (void);
65 static pascal void DBItemNotificationCallBack (HIViewRef, DataBrowserItemID, DataBrowserItemNotification);
66 static pascal Boolean DBCompareCallBack (HIViewRef, DataBrowserItemID, DataBrowserItemID, DataBrowserPropertyID);
67 static pascal OSStatus DBClientDataCallback (HIViewRef, DataBrowserItemID, DataBrowserPropertyID, DataBrowserItemDataRef, Boolean);
68 static pascal OSStatus CheatEventHandler (EventHandlerCallRef, EventRef, void *);
69
70
InitCheatItems(void)71 static void InitCheatItems (void)
72 {
73 for (unsigned int i = 0; i < MAC_MAX_CHEATS; i++)
74 {
75 citem[i].id = i + 1;
76 citem[i].valid = false;
77 citem[i].enabled = false;
78 citem[i].address = 0;
79 citem[i].value = 0;
80 sprintf(citem[i].description, "Cheat %03" PRIu32, citem[i].id);
81 }
82 }
83
ImportCheatItems(void)84 static void ImportCheatItems (void)
85 {
86 int cheat_num = std::min((int)Cheat.g.size(), MAC_MAX_CHEATS);
87 for (unsigned int i = 0; i < cheat_num; i++)
88 {
89 citem[i].valid = true;
90 citem[i].enabled = Cheat.g[i].enabled;
91 citem[i].address = Cheat.g[i].c[0].address; // mac dialog only supports one cheat per group at the moment
92 citem[i].value = Cheat.g[i].c[0].byte;
93 strncpy(citem[i].description, Cheat.g[i].name, 21);
94 citem[i].description[21] = '\0';
95 }
96 }
97
DetachCheatItems(void)98 static void DetachCheatItems (void)
99 {
100 S9xDeleteCheats();
101
102 for (unsigned int i = 0; i < MAC_MAX_CHEATS; i++)
103 {
104 if (citem[i].valid)
105 {
106 char code[10];
107 snprintf(code, 10, "%x=%x", citem[i].address, citem[i].value);
108 int index = S9xAddCheatGroup(citem[i].description, code);
109 if(citem[i].enabled && index >= 0)
110 S9xEnableCheatGroup(index);
111 }
112 }
113 }
114
ConfigureCheat(void)115 void ConfigureCheat (void)
116 {
117 if (!cartOpen)
118 return;
119
120 OSStatus err;
121 IBNibRef nibRef;
122
123 err = CreateNibReference(kMacS9XCFString, &nibRef);
124 if (err == noErr)
125 {
126 err = CreateWindowFromNib(nibRef, CFSTR("CheatEntry"), &wRef);
127 if (err == noErr)
128 {
129 DataBrowserCallbacks callbacks;
130 EventHandlerRef eref;
131 EventHandlerUPP eUPP;
132 EventTypeSpec events[] = { { kEventClassCommand, kEventCommandProcess },
133 { kEventClassCommand, kEventCommandUpdateStatus },
134 { kEventClassWindow, kEventWindowClose } };
135 HIViewRef ctl, root;
136 HIViewID cid;
137
138 root = HIViewGetRoot(wRef);
139 cid.id = 0;
140 cid.signature = kDataBrowser;
141 HIViewFindByID(root, cid, &dbRef);
142
143 #ifdef MAC_PANTHER_SUPPORT
144 if (systemVersion < 0x1040)
145 {
146 HISize minSize;
147 Rect rct;
148
149 GetWindowBounds(wRef, kWindowContentRgn, &rct);
150 minSize.width = (float) (rct.right - rct.left);
151 minSize.height = (float) (rct.bottom - rct.top );
152 err = SetWindowResizeLimits(wRef, &minSize, NULL);
153 }
154 #endif
155
156 callbacks.version = kDataBrowserLatestCallbacks;
157 err = InitDataBrowserCallbacks(&callbacks);
158 callbacks.u.v1.itemDataCallback = NewDataBrowserItemDataUPP(DBClientDataCallback);
159 callbacks.u.v1.itemCompareCallback = NewDataBrowserItemCompareUPP(DBCompareCallBack);
160 callbacks.u.v1.itemNotificationCallback = NewDataBrowserItemNotificationUPP(DBItemNotificationCallBack);
161 err = SetDataBrowserCallbacks(dbRef, &callbacks);
162
163 if (systemVersion >= 0x1040)
164 err = DataBrowserChangeAttributes(dbRef, kDataBrowserAttributeListViewAlternatingRowColors, kDataBrowserAttributeNone);
165
166 InitCheatItems();
167 ImportCheatItems();
168
169 DataBrowserItemID *id;
170
171 id = new DataBrowserItemID[MAC_MAX_CHEATS];
172 if (!id)
173 QuitWithFatalError(0, "cheat 01");
174
175 numofcheats = 0;
176
177 for (unsigned int i = 0; i < MAC_MAX_CHEATS; i++)
178 {
179 if (citem[i].valid)
180 {
181 id[numofcheats] = citem[i].id;
182 numofcheats++;
183 }
184 }
185
186 if (numofcheats)
187 err = AddDataBrowserItems(dbRef, kDataBrowserNoItem, numofcheats, id, kDataBrowserItemNoProperty);
188
189 delete [] id;
190
191 cid.signature = kNewButton;
192 HIViewFindByID(root, cid, &ctl);
193 if (numofcheats == MAC_MAX_CHEATS)
194 err = DeactivateControl(ctl);
195 else
196 err = ActivateControl(ctl);
197
198 cid.signature = kAllButton;
199 HIViewFindByID(root, cid, &ctl);
200 if (numofcheats == 0)
201 err = DeactivateControl(ctl);
202 else
203 err = ActivateControl(ctl);
204
205 cid.signature = kDelButton;
206 HIViewFindByID(root, cid, &ctl);
207 err = DeactivateControl(ctl);
208
209 eUPP = NewEventHandlerUPP(CheatEventHandler);
210 err = InstallWindowEventHandler(wRef, eUPP, GetEventTypeCount(events), events, (void *) wRef, &eref);
211
212 err = SetKeyboardFocus(wRef, dbRef, kControlFocusNextPart);
213
214 MoveWindowPosition(wRef, kWindowCheatEntry, true);
215 ShowWindow(wRef);
216 err = RunAppModalLoopForWindow(wRef);
217 HideWindow(wRef);
218 SaveWindowPosition(wRef, kWindowCheatEntry);
219
220 err = RemoveEventHandler(eref);
221 DisposeEventHandlerUPP(eUPP);
222
223 DisposeDataBrowserItemNotificationUPP(callbacks.u.v1.itemNotificationCallback);
224 DisposeDataBrowserItemCompareUPP(callbacks.u.v1.itemCompareCallback);
225 DisposeDataBrowserItemDataUPP(callbacks.u.v1.itemDataCallback);
226
227 CFRelease(wRef);
228
229 DetachCheatItems();
230 }
231
232 DisposeNibReference(nibRef);
233 }
234 }
235
AddCheatItem(void)236 static void AddCheatItem (void)
237 {
238 OSStatus err;
239 HIViewRef ctl, root;
240 HIViewID cid;
241 DataBrowserItemID id[1];
242 unsigned int i;
243
244 if (numofcheats == MAC_MAX_CHEATS)
245 return;
246
247 for (i = 0; i < MAC_MAX_CHEATS; i++)
248 if (citem[i].valid == false)
249 break;
250
251 if (i == MAC_MAX_CHEATS)
252 return;
253
254 numofcheats++;
255 citem[i].valid = true;
256 citem[i].enabled = false;
257 citem[i].address = 0;
258 citem[i].value = 0;
259 sprintf(citem[i].description, "Cheat %03" PRIu32, citem[i].id);
260
261 id[0] = citem[i].id;
262 err = AddDataBrowserItems(dbRef, kDataBrowserNoItem, 1, id, kDataBrowserItemNoProperty);
263 err = RevealDataBrowserItem(dbRef, id[0], kCmAddress, true);
264
265 root = HIViewGetRoot(wRef);
266 cid.id = 0;
267
268 if (numofcheats == MAC_MAX_CHEATS)
269 {
270 cid.signature = kNewButton;
271 HIViewFindByID(root, cid, &ctl);
272 err = DeactivateControl(ctl);
273 }
274
275 if (numofcheats)
276 {
277 cid.signature = kAllButton;
278 HIViewFindByID(root, cid, &ctl);
279 err = ActivateControl(ctl);
280 }
281 }
282
DeleteCheatItem(void)283 static void DeleteCheatItem (void)
284 {
285 OSStatus err;
286 HIViewRef ctl, root;
287 HIViewID cid;
288 Handle selectedItems;
289 ItemCount selectionCount;
290
291 selectedItems = NewHandle(0);
292 if (!selectedItems)
293 return;
294
295 err = GetDataBrowserItems(dbRef, kDataBrowserNoItem, true, kDataBrowserItemIsSelected, selectedItems);
296 selectionCount = (GetHandleSize(selectedItems) / sizeof(DataBrowserItemID));
297
298 if (selectionCount == 0)
299 {
300 DisposeHandle(selectedItems);
301 return;
302 }
303
304 err = RemoveDataBrowserItems(dbRef, kDataBrowserNoItem, selectionCount, (DataBrowserItemID *) *selectedItems, kDataBrowserItemNoProperty);
305
306 for (unsigned int i = 0; i < selectionCount; i++)
307 {
308 citem[((DataBrowserItemID *) (*selectedItems))[i] - 1].valid = false;
309 citem[((DataBrowserItemID *) (*selectedItems))[i] - 1].enabled = false;
310 numofcheats--;
311 }
312
313 DisposeHandle(selectedItems);
314
315 root = HIViewGetRoot(wRef);
316 cid.id = 0;
317
318 if (numofcheats < MAC_MAX_CHEATS)
319 {
320 cid.signature = kNewButton;
321 HIViewFindByID(root, cid, &ctl);
322 err = ActivateControl(ctl);
323 }
324
325 if (numofcheats == 0)
326 {
327 cid.signature = kAllButton;
328 HIViewFindByID(root, cid, &ctl);
329 err = DeactivateControl(ctl);
330 }
331 }
332
EnableAllCheatItems(void)333 static void EnableAllCheatItems (void)
334 {
335 OSStatus err;
336
337 for (unsigned int i = 0; i < MAC_MAX_CHEATS; i++)
338 if (citem[i].valid)
339 citem[i].enabled = true;
340
341 err = UpdateDataBrowserItems(dbRef, kDataBrowserNoItem, kDataBrowserNoItem, NULL, kDataBrowserItemNoProperty, kCmCheckBox);
342 }
343
DBClientDataCallback(HIViewRef browser,DataBrowserItemID itemID,DataBrowserPropertyID property,DataBrowserItemDataRef itemData,Boolean changeValue)344 static pascal OSStatus DBClientDataCallback (HIViewRef browser, DataBrowserItemID itemID, DataBrowserPropertyID property, DataBrowserItemDataRef itemData, Boolean changeValue)
345 {
346 OSStatus err, result;
347 CFStringRef str;
348 Boolean r;
349 uint32 address;
350 uint8 value;
351 char code[256];
352
353 result = noErr;
354
355 switch (property)
356 {
357 case kCmCheckBox:
358 ThemeButtonValue buttonValue;
359
360 if (changeValue)
361 {
362 err = GetDataBrowserItemDataButtonValue(itemData, &buttonValue);
363 citem[itemID - 1].enabled = (buttonValue == kThemeButtonOn) ? true : false;
364 }
365 else
366 err = SetDataBrowserItemDataButtonValue(itemData, citem[itemID - 1].enabled ? kThemeButtonOn : kThemeButtonOff);
367
368 break;
369
370 case kCmAddress:
371 if (changeValue)
372 {
373 err = GetDataBrowserItemDataText(itemData, &str);
374 r = CFStringGetCString(str, code, 256, CFStringGetSystemEncoding());
375 CFRelease(str);
376 if (r)
377 {
378 Boolean translated;
379
380 if (S9xProActionReplayToRaw(code, address, value) == NULL)
381 translated = true;
382 else
383 if (S9xGameGenieToRaw(code, address, value) == NULL)
384 translated = true;
385 else
386 {
387 translated = false;
388 if (sscanf(code, "%" SCNx32, &address) != 1)
389 address = 0;
390 else
391 address &= 0xFFFFFF;
392 }
393
394 citem[itemID - 1].address = address;
395 sprintf(code, "%06" PRIX32, address);
396 str = CFStringCreateWithCString(kCFAllocatorDefault, code, CFStringGetSystemEncoding());
397 err = SetDataBrowserItemDataText(itemData, str);
398 CFRelease(str);
399
400 if (translated)
401 {
402 DataBrowserItemID id[1];
403
404 citem[itemID - 1].value = value;
405 id[0] = itemID;
406 err = UpdateDataBrowserItems(browser, kDataBrowserNoItem, 1, id, kDataBrowserItemNoProperty, kCmValue);
407 }
408 }
409 }
410 else
411 {
412 sprintf(code, "%06" PRIX32, citem[itemID - 1].address);
413 str = CFStringCreateWithCString(kCFAllocatorDefault, code, CFStringGetSystemEncoding());
414 err = SetDataBrowserItemDataText(itemData, str);
415 CFRelease(str);
416 }
417
418 break;
419
420 case kCmValue:
421 if (changeValue)
422 {
423 err = GetDataBrowserItemDataText(itemData, &str);
424 r = CFStringGetCString(str, code, 256, CFStringGetSystemEncoding());
425 CFRelease(str);
426 if (r)
427 {
428 uint32 byte;
429
430 if (sscanf(code, "%" SCNx32, &byte) == 1)
431 citem[itemID - 1].value = (uint8) byte;
432 else
433 {
434 citem[itemID - 1].value = 0;
435 err = SetDataBrowserItemDataText(itemData, CFSTR("00"));
436 }
437 }
438 }
439 else
440 {
441 sprintf(code, "%02" PRIX8, citem[itemID - 1].value);
442 str = CFStringCreateWithCString(kCFAllocatorDefault, code, CFStringGetSystemEncoding());
443 err = SetDataBrowserItemDataText(itemData, str);
444 CFRelease(str);
445 }
446
447 break;
448
449 case kCmDescription:
450 if (changeValue)
451 {
452 code[0] = 0;
453 err = GetDataBrowserItemDataText(itemData, &str);
454 strcpy(code, GetMultiByteCharacters(str, 19));
455 CFRelease(str);
456
457 if (code[0] == 0)
458 {
459 code[0] = ' ';
460 code[1] = 0;
461 }
462
463 strcpy(citem[itemID - 1].description, code);
464 }
465 else
466 {
467 str = CFStringCreateWithCString(kCFAllocatorDefault, citem[itemID - 1].description, CFStringGetSystemEncoding());
468 err = SetDataBrowserItemDataText(itemData, str);
469 CFRelease(str);
470 }
471
472 break;
473
474 case kDataBrowserItemIsActiveProperty:
475 err = SetDataBrowserItemDataBooleanValue(itemData, true);
476 break;
477
478 case kDataBrowserItemIsEditableProperty:
479 err = SetDataBrowserItemDataBooleanValue(itemData, true);
480 break;
481
482 default:
483 result = errDataBrowserPropertyNotSupported;
484 }
485
486 return (result);
487 }
488
DBCompareCallBack(HIViewRef browser,DataBrowserItemID itemOne,DataBrowserItemID itemTwo,DataBrowserPropertyID sortProperty)489 static pascal Boolean DBCompareCallBack (HIViewRef browser, DataBrowserItemID itemOne, DataBrowserItemID itemTwo, DataBrowserPropertyID sortProperty)
490 {
491 Boolean result = false;
492
493 switch (sortProperty)
494 {
495 case kCmCheckBox:
496 result = (citem[itemOne - 1].enabled && !(citem[itemTwo - 1].enabled)) ? true : false;
497 break;
498
499 case kCmAddress:
500 result = (citem[itemOne - 1].address < citem[itemTwo - 1].address) ? true : false;
501 break;
502
503 case kCmValue:
504 result = (citem[itemOne - 1].value < citem[itemTwo - 1].value) ? true : false;
505 break;
506
507 case kCmDescription:
508 result = (strcmp(citem[itemOne - 1].description, citem[itemTwo - 1].description) < 0) ? true : false;
509 }
510
511 return (result);
512 }
513
DBItemNotificationCallBack(HIViewRef browser,DataBrowserItemID itemID,DataBrowserItemNotification message)514 static pascal void DBItemNotificationCallBack (HIViewRef browser, DataBrowserItemID itemID, DataBrowserItemNotification message)
515 {
516 OSStatus err;
517 HIViewRef ctl;
518 HIViewID cid = { kDelButton, 0 };
519 ItemCount selectionCount;
520
521 switch (message)
522 {
523 case kDataBrowserSelectionSetChanged:
524 HIViewFindByID(HIViewGetRoot(wRef), cid, &ctl);
525
526 err = GetDataBrowserItemCount(browser, kDataBrowserNoItem, true, kDataBrowserItemIsSelected, &selectionCount);
527 if (selectionCount == 0)
528 err = DeactivateControl(ctl);
529 else
530 err = ActivateControl(ctl);
531 }
532 }
533
CheatEventHandler(EventHandlerCallRef inHandlerCallRef,EventRef inEvent,void * inUserData)534 static pascal OSStatus CheatEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
535 {
536 OSStatus err, result = eventNotHandledErr;
537 WindowRef tWindowRef;
538
539 tWindowRef = (WindowRef) inUserData;
540
541 switch (GetEventClass(inEvent))
542 {
543 case kEventClassWindow:
544 switch (GetEventKind(inEvent))
545 {
546 case kEventWindowClose:
547 QuitAppModalLoopForWindow(tWindowRef);
548 result = noErr;
549 break;
550 }
551
552 break;
553
554 case kEventClassCommand:
555 switch (GetEventKind(inEvent))
556 {
557 HICommand tHICommand;
558
559 case kEventCommandUpdateStatus:
560 err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand);
561 if (err == noErr && tHICommand.commandID == 'clos')
562 {
563 UpdateMenuCommandStatus(true);
564 result = noErr;
565 }
566
567 break;
568
569 case kEventCommandProcess:
570 err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand);
571 if (err == noErr)
572 {
573 switch (tHICommand.commandID)
574 {
575 case kNewButton:
576 AddCheatItem();
577 result = noErr;
578 break;
579
580 case kDelButton:
581 DeleteCheatItem();
582 result = noErr;
583 break;
584
585 case kAllButton:
586 EnableAllCheatItems();
587 result = noErr;
588 }
589 }
590 }
591 }
592
593 return (result);
594 }
595