1 #include "burner.h"
2
3 #define NUM_LANGUAGES 12
4 #define MAX_NODES 1024
5 #define MAX_ACTIVE_PATCHES 1024
6
7 static HWND hIpsDlg = NULL;
8 static HWND hParent = NULL;
9 static HWND hIpsList = NULL;
10
11 int nIpsSelectedLanguage = 0;
12 static TCHAR szFullName[1024];
13 static TCHAR szLanguages[NUM_LANGUAGES][32];
14 static TCHAR szLanguageCodes[NUM_LANGUAGES][6];
15
16 static HTREEITEM hItemHandles[MAX_NODES];
17
18 static int nPatchIndex = 0;
19 static int nNumPatches = 0;
20 static HTREEITEM hPatchHandlesIndex[MAX_NODES];
21 static TCHAR szPatchFileNames[MAX_NODES][MAX_PATH];
22
23 static HBRUSH hWhiteBGBrush;
24 static HBITMAP hBmp = NULL;
25 static HBITMAP hPreview = NULL;
26
27 static TCHAR szDriverName[32];
28
29 INT32 nIpsMaxFileLen = 0;
30
31 TCHAR szIpsActivePatches[MAX_ACTIVE_PATCHES][MAX_PATH];
32
33 // GCC doesn't seem to define these correctly.....
34 #define _TreeView_SetItemState(hwndTV, hti, data, _mask) \
35 { TVITEM _ms_TVi;\
36 _ms_TVi.mask = TVIF_STATE; \
37 _ms_TVi.hItem = hti; \
38 _ms_TVi.stateMask = _mask;\
39 _ms_TVi.state = data;\
40 SNDMSG((hwndTV), TVM_SETITEM, 0, (LPARAM)(TV_ITEM *)&_ms_TVi);\
41 }
42
43 #define _TreeView_SetCheckState(hwndTV, hti, fCheck) \
44 _TreeView_SetItemState(hwndTV, hti, INDEXTOSTATEIMAGEMASK((fCheck)?2:1), TVIS_STATEIMAGEMASK)
45
46 #define _TreeView_GetCheckState(hwndTV, hti) \
47 ((((UINT)(SNDMSG((hwndTV), TVM_GETITEMSTATE, (WPARAM)(hti), TVIS_STATEIMAGEMASK))) >> 12) -1)
48
GameIpsConfigName()49 static TCHAR* GameIpsConfigName()
50 {
51 // Return the path of the config file for this game
52 static TCHAR szName[64];
53 _stprintf(szName, _T("config\\ips\\%s.ini"), szDriverName);
54 return szName;
55 }
56
GetIpsNumPatches()57 int GetIpsNumPatches()
58 {
59 WIN32_FIND_DATA wfd;
60 HANDLE hSearch;
61 TCHAR szFilePath[MAX_PATH];
62 int Count = 0;
63
64 _stprintf(szFilePath, _T("%s%s\\"), szAppIpsPath, BurnDrvGetText(DRV_NAME));
65 _tcscat(szFilePath, _T("*.dat"));
66
67 hSearch = FindFirstFile(szFilePath, &wfd);
68
69 if (hSearch != INVALID_HANDLE_VALUE) {
70 int Done = 0;
71
72 while (!Done ) {
73 Count++;
74 Done = !FindNextFile(hSearch, &wfd);
75 }
76
77 FindClose(hSearch);
78 }
79
80 return Count;
81 }
82
GetPatchDescByLangcode(FILE * fp,int nLang)83 static TCHAR* GetPatchDescByLangcode(FILE* fp, int nLang)
84 {
85 TCHAR* result = NULL;
86 char* desc = NULL;
87 char langtag[10];
88
89 sprintf(langtag, "[%s]", TCHARToANSI(szLanguageCodes[nLang], NULL, 0));
90
91 fseek(fp, 0, SEEK_SET);
92
93 while (!feof(fp))
94 {
95 char s[4096];
96
97 if (fgets(s, sizeof(s), fp) != NULL)
98 {
99 if (strncmp(langtag, s, strlen(langtag)) != 0)
100 continue;
101
102 while (fgets(s, sizeof(s), fp) != NULL)
103 {
104 char* p;
105
106 if (*s == '[')
107 {
108 if (desc)
109 {
110 result = tstring_from_utf8(desc);
111 if (desc) {
112 free(desc);
113 desc = NULL;
114 }
115 return result;
116 }
117 else
118 return NULL;
119 }
120
121 for (p = s; *p; p++)
122 {
123 if (*p == '\r' || *p == '\n')
124 {
125 *p = '\0';
126 break;
127 }
128 }
129
130 if (desc)
131 {
132 char* p1;
133 int len = strlen(desc);
134
135 len += strlen(s) + 2;
136 p1 = (char*)malloc(len + 1);
137 sprintf(p1, "%s\r\n%s", desc, s);
138 if (desc) {
139 free(desc);
140 }
141 desc = p1;
142 }
143 else
144 {
145 desc = (char*)malloc(strlen(s) + 1);
146 if (desc != NULL)
147 strcpy(desc, s);
148 }
149 }
150 }
151 }
152
153 if (desc)
154 {
155 result = tstring_from_utf8(desc);
156 if (desc) {
157 free(desc);
158 desc = NULL;
159 }
160 return result;
161 }
162 else
163 return NULL;
164 }
165
FillListBox()166 static void FillListBox()
167 {
168 WIN32_FIND_DATA wfd;
169 HANDLE hSearch;
170 TCHAR szFilePath[MAX_PATH];
171 TCHAR szFilePathSearch[MAX_PATH];
172 TCHAR szFileName[MAX_PATH];
173 TCHAR *PatchDesc = NULL;
174 TCHAR PatchName[256];
175 int nHandlePos = 0;
176
177 TV_INSERTSTRUCT TvItem;
178
179 memset(&TvItem, 0, sizeof(TvItem));
180 TvItem.item.mask = TVIF_TEXT | TVIF_PARAM;
181 TvItem.hInsertAfter = TVI_LAST;
182
183 _stprintf(szFilePath, _T("%s%s\\"), szAppIpsPath, szDriverName);
184 _stprintf(szFilePathSearch, _T("%s*.dat"), szFilePath);
185
186 hSearch = FindFirstFile(szFilePathSearch, &wfd);
187
188 if (hSearch != INVALID_HANDLE_VALUE) {
189 int Done = 0;
190
191 while (!Done ) {
192 memset(szFileName, '\0', MAX_PATH * sizeof(TCHAR));
193 _stprintf(szFileName, _T("%s%s"), szFilePath, wfd.cFileName);
194
195 FILE *fp = _tfopen(szFileName, _T("r"));
196 if (fp) {
197 bool AllocDesc = false;
198 PatchDesc = NULL;
199 memset(PatchName, '\0', 256 * sizeof(TCHAR));
200
201 PatchDesc = GetPatchDescByLangcode(fp, nIpsSelectedLanguage);
202 // If not available - try English first
203 if (PatchDesc == NULL) PatchDesc = GetPatchDescByLangcode(fp, 0);
204 // Simplified Chinese is the reference language (should always be available!!)
205 if (PatchDesc == NULL) PatchDesc = GetPatchDescByLangcode(fp, 1);
206
207 bprintf(0, _T("PatchDesc [%s]\n"), PatchDesc);
208
209 if (PatchDesc == NULL) {
210 PatchDesc = (TCHAR*)malloc(1024);
211 memset(PatchDesc, 0, 1024);
212 AllocDesc = true;
213 _stprintf(PatchDesc, _T("%s"), wfd.cFileName);
214 }
215
216 for (unsigned int i = 0; i < _tcslen(PatchDesc); i++) {
217 if (PatchDesc[i] == '\r' || PatchDesc[i] == '\n') break;
218 PatchName[i] = PatchDesc[i];
219 }
220
221 if (AllocDesc) {
222 free(PatchDesc);
223 }
224
225 // Check for categories
226 TCHAR *Tokens;
227 int nNumTokens = 0;
228 int nNumNodes = 0;
229 TCHAR szCategory[256];
230 unsigned int nPatchNameLength = _tcslen(PatchName);
231
232 Tokens = _tcstok(PatchName, _T("/"));
233 while (Tokens != NULL) {
234 if (nNumTokens == 0) {
235 int bAddItem = 1;
236 // Check if item already exists
237 nNumNodes = SendMessage(hIpsList, TVM_GETCOUNT, (WPARAM)0, (LPARAM)0);
238 for (int i = 0; i < nNumNodes; i++) {
239 TCHAR Temp[256];
240 TVITEM Tvi;
241 memset(&Tvi, 0, sizeof(Tvi));
242 Tvi.hItem = hItemHandles[i];
243 Tvi.mask = TVIF_TEXT | TVIF_HANDLE;
244 Tvi.pszText = Temp;
245 Tvi.cchTextMax = 256;
246 SendMessage(hIpsList, TVM_GETITEM, (WPARAM)0, (LPARAM)&Tvi);
247
248 if (!_tcsicmp(Tvi.pszText, Tokens)) bAddItem = 0;
249 }
250
251 if (bAddItem) {
252 TvItem.hParent = TVI_ROOT;
253 TvItem.item.pszText = Tokens;
254 hItemHandles[nHandlePos] = (HTREEITEM)SendMessage(hIpsList, TVM_INSERTITEM, 0, (LPARAM)&TvItem);
255 nHandlePos++;
256 }
257
258 if (_tcslen(Tokens) == nPatchNameLength) {
259 hPatchHandlesIndex[nPatchIndex] = hItemHandles[nHandlePos - 1];
260 _tcscpy(szPatchFileNames[nPatchIndex], szFileName);
261
262 nPatchIndex++;
263 }
264
265 _tcscpy(szCategory, Tokens);
266 } else {
267 HTREEITEM hNode = TVI_ROOT;
268 // See which category we should be in
269 nNumNodes = SendMessage(hIpsList, TVM_GETCOUNT, (WPARAM)0, (LPARAM)0);
270 for (int i = 0; i < nNumNodes; i++) {
271 TCHAR Temp[256];
272 TVITEM Tvi;
273 memset(&Tvi, 0, sizeof(Tvi));
274 Tvi.hItem = hItemHandles[i];
275 Tvi.mask = TVIF_TEXT | TVIF_HANDLE;
276 Tvi.pszText = Temp;
277 Tvi.cchTextMax = 256;
278 SendMessage(hIpsList, TVM_GETITEM, (WPARAM)0, (LPARAM)&Tvi);
279
280 if (!_tcsicmp(Tvi.pszText, szCategory)) hNode = Tvi.hItem;
281 }
282
283 TvItem.hParent = hNode;
284 TvItem.item.pszText = Tokens;
285 hItemHandles[nHandlePos] = (HTREEITEM)SendMessage(hIpsList, TVM_INSERTITEM, 0, (LPARAM)&TvItem);
286
287 hPatchHandlesIndex[nPatchIndex] = hItemHandles[nHandlePos];
288 _tcscpy(szPatchFileNames[nPatchIndex], szFileName);
289
290 nHandlePos++;
291 nPatchIndex++;
292 }
293
294 Tokens = _tcstok(NULL, _T("/"));
295 nNumTokens++;
296 }
297
298 fclose(fp);
299 }
300
301 Done = !FindNextFile(hSearch, &wfd);
302 }
303
304 FindClose(hSearch);
305 }
306
307 nNumPatches = nPatchIndex;
308
309 // Expand all branches
310 int nNumNodes = SendMessage(hIpsList, TVM_GETCOUNT, (WPARAM)0, (LPARAM)0);;
311 for (int i = 0; i < nNumNodes; i++) {
312 SendMessage(hIpsList, TVM_EXPAND, TVE_EXPAND, (LPARAM)hItemHandles[i]);
313 }
314 }
315
GetIpsNumActivePatches()316 int GetIpsNumActivePatches()
317 {
318 int nActivePatches = 0;
319
320 for (int i = 0; i < MAX_ACTIVE_PATCHES; i++) {
321 if (_tcsicmp(szIpsActivePatches[i], _T(""))) nActivePatches++;
322 }
323
324 return nActivePatches;
325 }
326
LoadIpsActivePatches()327 void LoadIpsActivePatches()
328 {
329 _tcscpy(szDriverName, BurnDrvGetText(DRV_NAME));
330
331 for (int i = 0; i < MAX_ACTIVE_PATCHES; i++) {
332 _stprintf(szIpsActivePatches[i], _T(""));
333 }
334
335 FILE* fp = _tfopen(GameIpsConfigName(), _T("rt"));
336 TCHAR szLine[MAX_PATH];
337 int nActivePatches = 0;
338
339 if (fp) {
340 while (_fgetts(szLine, sizeof(szLine), fp)) {
341 int nLen = _tcslen(szLine);
342
343 // Get rid of the linefeed at the end
344 if (szLine[nLen - 1] == 10) {
345 szLine[nLen - 1] = 0;
346 nLen--;
347 }
348
349 if (!_tcsnicmp(szLine, _T("//"), 2)) continue;
350 if (!_tcsicmp(szLine, _T(""))) continue;
351
352 _stprintf(szIpsActivePatches[nActivePatches], _T("%s%s\\%s"), szAppIpsPath, szDriverName, szLine);
353 nActivePatches++;
354 }
355
356 fclose(fp);
357 }
358 }
359
CheckActivePatches()360 static void CheckActivePatches()
361 {
362 LoadIpsActivePatches();
363
364 int nActivePatches = GetIpsNumActivePatches();
365
366 for (int i = 0; i < nActivePatches; i++) {
367 for (int j = 0; j < nNumPatches; j++) {
368 if (!_tcsicmp(szIpsActivePatches[i], szPatchFileNames[j])) {
369 _TreeView_SetCheckState(hIpsList, hPatchHandlesIndex[j], TRUE);
370 }
371 }
372 }
373 }
374
IpsManagerInit()375 static int IpsManagerInit()
376 {
377 // Get the games full name
378 TCHAR szText[1024] = _T("");
379 TCHAR* pszPosition = szText;
380 TCHAR* pszName = BurnDrvGetText(DRV_FULLNAME);
381
382 pszPosition += _sntprintf(szText, 1024, pszName);
383
384 pszName = BurnDrvGetText(DRV_FULLNAME);
385 while ((pszName = BurnDrvGetText(DRV_NEXTNAME | DRV_FULLNAME)) != NULL) {
386 if (pszPosition + _tcslen(pszName) - 1024 > szText) {
387 break;
388 }
389 pszPosition += _stprintf(pszPosition, _T(SEPERATOR_2) _T("%s"), pszName);
390 }
391
392 _tcscpy(szFullName, szText);
393
394 _stprintf(szText, _T("%s") _T(SEPERATOR_1) _T("%s"), FBALoadStringEx(hAppInst, IDS_IPSMANAGER_TITLE, true), szFullName);
395
396 // Set the window caption
397 SetWindowText(hIpsDlg, szText);
398
399 // Fill the combo box
400 _stprintf(szLanguages[0], FBALoadStringEx(hAppInst, IDS_LANG_ENGLISH_US, true));
401 _stprintf(szLanguages[1], FBALoadStringEx(hAppInst, IDS_LANG_SIMP_CHINESE, true));
402 _stprintf(szLanguages[2], FBALoadStringEx(hAppInst, IDS_LANG_TRAD_CHINESE, true));
403 _stprintf(szLanguages[3], FBALoadStringEx(hAppInst, IDS_LANG_JAPANESE, true));
404 _stprintf(szLanguages[4], FBALoadStringEx(hAppInst, IDS_LANG_KOREAN, true));
405 _stprintf(szLanguages[5], FBALoadStringEx(hAppInst, IDS_LANG_FRENCH, true));
406 _stprintf(szLanguages[6], FBALoadStringEx(hAppInst, IDS_LANG_SPANISH, true));
407 _stprintf(szLanguages[7], FBALoadStringEx(hAppInst, IDS_LANG_ITALIAN, true));
408 _stprintf(szLanguages[8], FBALoadStringEx(hAppInst, IDS_LANG_GERMAN, true));
409 _stprintf(szLanguages[9], FBALoadStringEx(hAppInst, IDS_LANG_PORTUGUESE, true));
410 _stprintf(szLanguages[10], FBALoadStringEx(hAppInst, IDS_LANG_POLISH, true));
411 _stprintf(szLanguages[11], FBALoadStringEx(hAppInst, IDS_LANG_HUNGARIAN, true));
412
413 _stprintf(szLanguageCodes[0], _T("en_US"));
414 _stprintf(szLanguageCodes[1], _T("zh_CN"));
415 _stprintf(szLanguageCodes[2], _T("zh_TW"));
416 _stprintf(szLanguageCodes[3], _T("ja_JP"));
417 _stprintf(szLanguageCodes[4], _T("ko_KR"));
418 _stprintf(szLanguageCodes[5], _T("fr_FR"));
419 _stprintf(szLanguageCodes[6], _T("es_ES"));
420 _stprintf(szLanguageCodes[7], _T("it_IT"));
421 _stprintf(szLanguageCodes[8], _T("de_DE"));
422 _stprintf(szLanguageCodes[9], _T("pt_BR"));
423 _stprintf(szLanguageCodes[10], _T("pl_PL"));
424 _stprintf(szLanguageCodes[11], _T("hu_HU"));
425
426 for (int i = 0; i < NUM_LANGUAGES; i++) {
427 SendDlgItemMessage(hIpsDlg, IDC_CHOOSE_LIST, CB_ADDSTRING, 0, (LPARAM)&szLanguages[i]);
428 }
429
430 SendDlgItemMessage(hIpsDlg, IDC_CHOOSE_LIST, CB_SETCURSEL, (WPARAM)nIpsSelectedLanguage, (LPARAM)0);
431
432 hIpsList = GetDlgItem(hIpsDlg, IDC_TREE1);
433
434 _tcscpy(szDriverName, BurnDrvGetText(DRV_NAME));
435
436 FillListBox();
437
438 CheckActivePatches();
439
440 return 0;
441 }
442
RefreshPatch()443 static void RefreshPatch()
444 {
445 SendMessage(GetDlgItem(hIpsDlg, IDC_TEXTCOMMENT), WM_SETTEXT, (WPARAM)0, (LPARAM)NULL);
446 SendDlgItemMessage(hIpsDlg, IDC_SCREENSHOT_H, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hPreview);
447
448 HTREEITEM hSelectHandle = (HTREEITEM)SendMessage(hIpsList, TVM_GETNEXTITEM, TVGN_CARET, ~0U);
449
450 if (hBmp) {
451 DeleteObject((HGDIOBJ)hBmp);
452 hBmp = NULL;
453 }
454
455 for (int i = 0; i < nNumPatches; i++) {
456 if (hSelectHandle == hPatchHandlesIndex[i]) {
457 TCHAR *PatchDesc = NULL;
458
459 FILE *fp = _tfopen(szPatchFileNames[i], _T("r"));
460 if (fp) {
461 PatchDesc = GetPatchDescByLangcode(fp, nIpsSelectedLanguage);
462 // If not available - try English first
463 if (PatchDesc == NULL) PatchDesc = GetPatchDescByLangcode(fp, 0);
464 // Simplified Chinese is the reference language (should always be available!!)
465 if (PatchDesc == NULL) PatchDesc = GetPatchDescByLangcode(fp, 1);
466
467 SendMessage(GetDlgItem(hIpsDlg, IDC_TEXTCOMMENT), WM_SETTEXT, (WPARAM)0, (LPARAM)PatchDesc);
468
469 fclose(fp);
470 }
471 fp = NULL;
472
473 TCHAR szImageFileName[MAX_PATH];
474 szImageFileName[0] = _T('\0');
475
476 _tcscpy(szImageFileName, szPatchFileNames[i]);
477 szImageFileName[_tcslen(szImageFileName) - 3] = _T('p');
478 szImageFileName[_tcslen(szImageFileName) - 2] = _T('n');
479 szImageFileName[_tcslen(szImageFileName) - 1] = _T('g');
480
481 fp = _tfopen(szImageFileName, _T("rb"));
482 HBITMAP hNewImage = NULL;
483 if (fp) {
484 hNewImage = PNGLoadBitmap(hIpsDlg, fp, 304, 228, 3);
485 fclose(fp);
486 }
487
488 if (hNewImage) {
489 DeleteObject((HGDIOBJ)hBmp);
490 hBmp = hNewImage;
491 SendDlgItemMessage(hIpsDlg, IDC_SCREENSHOT_H, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBmp);
492 } else {
493 SendDlgItemMessage(hIpsDlg, IDC_SCREENSHOT_H, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hPreview);
494 }
495 }
496 }
497 }
498
SavePatches()499 static void SavePatches()
500 {
501 int nActivePatches = 0;
502
503 for (int i = 0; i < MAX_ACTIVE_PATCHES; i++) {
504 _stprintf(szIpsActivePatches[i], _T(""));
505 }
506
507 for (int i = 0; i < nNumPatches; i++) {
508 int nChecked = _TreeView_GetCheckState(hIpsList, hPatchHandlesIndex[i]);
509
510 if (nChecked) {
511 _tcscpy(szIpsActivePatches[nActivePatches], szPatchFileNames[i]);
512 nActivePatches++;
513 }
514 }
515
516 FILE* fp = _tfopen(GameIpsConfigName(), _T("wt"));
517
518 if (fp) {
519 _ftprintf(fp, _T("// ") _T(APP_TITLE) _T(" v%s --- IPS Config File for %s (%s)\n\n"), szAppBurnVer, szDriverName, szFullName);
520 for (int i = 0; i < nActivePatches; i++) {
521 TCHAR *Tokens;
522 TCHAR szFileName[MAX_PATH];
523 Tokens = _tcstok(szIpsActivePatches[i], _T("\\"));
524 while (Tokens != NULL) {
525 szFileName[0] = _T('\0');
526 _tcscpy(szFileName, Tokens);
527 Tokens = _tcstok(NULL, _T("\\"));
528 }
529
530 _ftprintf(fp, _T("%s\n"), szFileName);
531 }
532 fclose(fp);
533 }
534 }
535
IpsManagerExit()536 static void IpsManagerExit()
537 {
538 SendDlgItemMessage(hIpsDlg, IDC_SCREENSHOT_H, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)NULL);
539
540 for (int i = 0; i < NUM_LANGUAGES; i++) {
541 szLanguages[i][0] = _T('\0');
542 szLanguageCodes[i][0] = _T('\0');
543 }
544
545 memset(hItemHandles, 0, MAX_NODES * sizeof(HTREEITEM));
546 memset(hPatchHandlesIndex, 0, MAX_NODES * sizeof(HTREEITEM));
547
548 nPatchIndex = 0;
549 nNumPatches = 0;
550
551 for (int i = 0; i < MAX_NODES; i++) {
552 szPatchFileNames[i][0] = _T('\0');
553 }
554
555 if (hBmp) {
556 DeleteObject((HGDIOBJ)hBmp);
557 hBmp = NULL;
558 }
559
560 if (hPreview) {
561 DeleteObject((HGDIOBJ)hPreview);
562 hPreview = NULL;
563 }
564
565 DeleteObject(hWhiteBGBrush);
566
567 hParent = NULL;
568
569 EndDialog(hIpsDlg, 0);
570 }
571
IpsOkay()572 static void IpsOkay()
573 {
574 SavePatches();
575 IpsManagerExit();
576 }
577
DefInpProc(HWND hDlg,UINT Msg,WPARAM wParam,LPARAM lParam)578 static INT_PTR CALLBACK DefInpProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
579 {
580 switch (Msg) {
581 case WM_INITDIALOG: {
582 hIpsDlg = hDlg;
583
584 hWhiteBGBrush = CreateSolidBrush(RGB(0xFF,0xFF,0xFF));
585 hPreview = PNGLoadBitmap(hIpsDlg, NULL, 304, 228, 2);
586 SendDlgItemMessage(hIpsDlg, IDC_SCREENSHOT_H, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hPreview);
587
588 LONG_PTR Style;
589 Style = GetWindowLongPtr (GetDlgItem(hIpsDlg, IDC_TREE1), GWL_STYLE);
590 Style |= TVS_CHECKBOXES;
591 SetWindowLongPtr (GetDlgItem(hIpsDlg, IDC_TREE1), GWL_STYLE, Style);
592
593 IpsManagerInit();
594
595 WndInMid(hDlg, hScrnWnd);
596 SetFocus(hDlg); // Enable Esc=close
597 break;
598 }
599
600 case WM_COMMAND: {
601 int wID = LOWORD(wParam);
602 int Notify = HIWORD(wParam);
603
604 if (Notify == BN_CLICKED) {
605 switch (wID) {
606 case IDOK: {
607 IpsOkay();
608 break;
609 }
610
611 case IDCANCEL: {
612 SendMessage(hDlg, WM_CLOSE, 0, 0);
613 return 0;
614 }
615
616 case IDC_IPSMAN_DESELECTALL: {
617 for (int i = 0; i < nNumPatches; i++) {
618 for (int j = 0; j < nNumPatches; j++) {
619 _TreeView_SetCheckState(hIpsList, hPatchHandlesIndex[j], FALSE);
620 }
621 }
622 break;
623 }
624 }
625 }
626
627 if (wID == IDC_CHOOSE_LIST && Notify == CBN_SELCHANGE) {
628 nIpsSelectedLanguage = SendMessage(GetDlgItem(hIpsDlg, IDC_CHOOSE_LIST), CB_GETCURSEL, 0, 0);
629 TreeView_DeleteAllItems(hIpsList);
630 FillListBox();
631 RefreshPatch();
632 return 0;
633 }
634
635 break;
636 }
637
638 case WM_NOTIFY: {
639 NMHDR* pNmHdr = (NMHDR*)lParam;
640
641 if (LOWORD(wParam) == IDC_TREE1 && pNmHdr->code == TVN_SELCHANGED) {
642 RefreshPatch();
643
644 return 1;
645 }
646
647 if (LOWORD(wParam) == IDC_TREE1 && pNmHdr->code == NM_DBLCLK) {
648 // disable double-click node-expand
649 SetWindowLongPtr(hIpsDlg, DWLP_MSGRESULT, 1);
650
651 return 1;
652 }
653
654 if (LOWORD(wParam) == IDC_TREE1 && pNmHdr->code == NM_CLICK) {
655 POINT cursorPos;
656 GetCursorPos(&cursorPos);
657 ScreenToClient(hIpsList, &cursorPos);
658
659 TVHITTESTINFO thi;
660 thi.pt = cursorPos;
661 TreeView_HitTest(hIpsList, &thi);
662
663 if (thi.flags == TVHT_ONITEMSTATEICON) {
664 TreeView_SelectItem(hIpsList, thi.hItem);
665 }
666
667 return 1;
668 }
669
670 SetWindowLongPtr(hIpsDlg, DWLP_MSGRESULT, CDRF_DODEFAULT);
671 return 1;
672 }
673
674 case WM_CTLCOLORSTATIC: {
675 if ((HWND)lParam == GetDlgItem(hIpsDlg, IDC_TEXTCOMMENT)) {
676 return (INT_PTR)hWhiteBGBrush;
677 }
678 break;
679 }
680
681 case WM_CLOSE: {
682 IpsManagerExit();
683 break;
684 }
685 }
686
687 return 0;
688 }
689
IpsManagerCreate(HWND hParentWND)690 int IpsManagerCreate(HWND hParentWND)
691 {
692 hParent = hParentWND;
693
694 FBADialogBox(hAppInst, MAKEINTRESOURCE(IDD_IPS_MANAGER), hParent, (DLGPROC)DefInpProc);
695 return 1;
696 }
697
698 // Game patching
699
700 #define UTF8_SIGNATURE "\xef\xbb\xbf"
701 #define IPS_SIGNATURE "PATCH"
702 #define IPS_TAG_EOF "EOF"
703 #define IPS_EXT ".ips"
704
705 #define BYTE3_TO_UINT(bp) \
706 (((unsigned int)(bp)[0] << 16) & 0x00FF0000) | \
707 (((unsigned int)(bp)[1] << 8) & 0x0000FF00) | \
708 ((unsigned int)(bp)[2] & 0x000000FF)
709
710 #define BYTE2_TO_UINT(bp) \
711 (((unsigned int)(bp)[0] << 8) & 0xFF00) | \
712 ((unsigned int) (bp)[1] & 0x00FF)
713
714 bool bDoIpsPatch = FALSE;
715
PatchFile(const char * ips_path,UINT8 * base,bool readonly)716 static void PatchFile(const char* ips_path, UINT8* base, bool readonly)
717 {
718 char buf[6];
719 FILE* f = NULL;
720 int Offset, Size;
721 UINT8* mem8 = NULL;
722
723 if (NULL == (f = fopen(ips_path, "rb")))
724 return;
725
726 memset(buf, 0, sizeof(buf));
727 fread(buf, 1, 5, f);
728 if (strcmp(buf, IPS_SIGNATURE)) {
729 bprintf(0, _T("IPS - Bad IPS-Signature in: %S.\n"), ips_path);
730 if (f)
731 {
732 fclose(f);
733 }
734 return;
735 } else {
736 bprintf(0, _T("IPS - Patching with: %S.\n"), ips_path);
737 UINT8 ch = 0;
738 int bRLE = 0;
739 while (!feof(f)) {
740 // read patch address offset
741 fread(buf, 1, 3, f);
742 buf[3] = 0;
743 if (strcmp(buf, IPS_TAG_EOF) == 0)
744 break;
745
746 Offset = BYTE3_TO_UINT(buf);
747
748 // read patch length
749 fread(buf, 1, 2, f);
750 Size = BYTE2_TO_UINT(buf);
751
752 bRLE = (Size == 0);
753 if (bRLE) {
754 fread(buf, 1, 2, f);
755 Size = BYTE2_TO_UINT(buf);
756 ch = fgetc(f);
757 }
758
759 while (Size--) {
760 mem8 = base + Offset;
761 Offset++;
762 if (Offset > nIpsMaxFileLen) nIpsMaxFileLen = Offset; // file size is growing
763 if (readonly) {
764 if (!bRLE) fgetc(f);
765 } else {
766 *mem8 = bRLE ? ch : fgetc(f);
767 }
768 }
769 }
770 }
771
772 fclose(f);
773 }
774
stristr_int(const char * str1,const char * str2)775 static char* stristr_int(const char* str1, const char* str2)
776 {
777 const char* p1 = str1;
778 const char* p2 = str2;
779 const char* r = (!*p2) ? str1 : NULL;
780
781 while (*p1 && *p2) {
782 if (tolower((unsigned char)*p1) == tolower((unsigned char)*p2)) {
783 if (!r) {
784 r = p1;
785 }
786
787 p2++;
788 } else {
789 p2 = str2;
790 if (r) {
791 p1 = r + 1;
792 }
793
794 if (tolower((unsigned char)*p1) == tolower((unsigned char)*p2)) {
795 r = p1;
796 p2++;
797 } else {
798 r = NULL;
799 }
800 }
801
802 p1++;
803 }
804
805 return (*p2) ? NULL : (char*)r;
806 }
807
DoPatchGame(const char * patch_name,char * game_name,UINT8 * base,INT32 readonly)808 static void DoPatchGame(const char* patch_name, char* game_name, UINT8* base, INT32 readonly)
809 {
810 char s[MAX_PATH];
811 char* p = NULL;
812 char* rom_name = NULL;
813 char* ips_name = NULL;
814 FILE* fp = NULL;
815 unsigned long nIpsSize;
816
817 if ((fp = fopen(patch_name, "rb")) != NULL) {
818 // get ips size
819 fseek(fp, 0, SEEK_END);
820 nIpsSize = ftell(fp);
821 fseek(fp, 0, SEEK_SET);
822
823 while (!feof(fp)) {
824 if (fgets(s, sizeof(s), fp) != NULL) {
825 p = s;
826
827 // skip UTF-8 sig
828 if (strncmp(p, UTF8_SIGNATURE, strlen(UTF8_SIGNATURE)) == 0)
829 p += strlen(UTF8_SIGNATURE);
830
831 if (p[0] == '[') // '['
832 break;
833
834 // Can support linetypes:
835 // "rom name.bin" "patch file.ips" CRC(abcd1234)
836 // romname.bin patchfile CRC(abcd1234)
837
838 if (p[0] == '\"') { // "quoted rom name with spaces.bin"
839 p++;
840 rom_name = strtok(p, "\"");
841 } else {
842 rom_name = strtok(p, " \t\r\n");
843 }
844
845 if (!rom_name)
846 continue;
847 if (*rom_name == '#')
848 continue;
849 if (_stricmp(rom_name, game_name))
850 continue;
851
852 ips_name = strtok(NULL, "\r\n");
853
854 if (!ips_name)
855 continue;
856
857 // remove crc portion, and end quote/spaces from ips name
858 char *c = stristr_int(ips_name, "crc");
859 if (c) {
860 c--; // "derp.ips" CRC(abcd1234)\n"
861 // ^ we're now here.
862 while (*c && (*c == ' ' || *c == '\t' || *c == '\"'))
863 {
864 *c = '\0';
865 c--;
866 }
867 }
868
869 // clean-up IPS name beginning (could be quoted or not)
870 while (ips_name && (ips_name[0] == ' ' || ips_name[0] == '\"'))
871 ips_name++;
872
873 char *has_ext = stristr_int(ips_name, ".ips");
874
875 bprintf(0, _T("ips name:[%S]\n"), ips_name);
876 bprintf(0, _T("rom name:[%S]\n"), rom_name);
877
878 char ips_path[MAX_PATH*2];
879 char ips_dir[MAX_PATH];
880 TCHARToANSI(szAppIpsPath, ips_dir, sizeof(ips_dir));
881
882 if (strchr(ips_name, '\\')) {
883 // ips in parent's folder
884 sprintf(ips_path, "%s\\%s%s", ips_dir, ips_name, (has_ext) ? "" : IPS_EXT);
885 } else {
886 sprintf(ips_path, "%s%s\\%s%s", ips_dir, BurnDrvGetTextA(DRV_NAME), ips_name, (has_ext) ? "" : IPS_EXT);
887 }
888
889 PatchFile(ips_path, base, readonly);
890 }
891 }
892 fclose(fp);
893 }
894 }
895
IpsApplyPatches(UINT8 * base,char * rom_name)896 void IpsApplyPatches(UINT8* base, char* rom_name)
897 {
898 char ips_data[MAX_PATH];
899
900 nIpsMaxFileLen = 0;
901
902 int nActivePatches = GetIpsNumActivePatches();
903
904 for (int i = 0; i < nActivePatches; i++) {
905 memset(ips_data, 0, MAX_PATH);
906 TCHARToANSI(szIpsActivePatches[i], ips_data, sizeof(ips_data));
907 DoPatchGame(ips_data, rom_name, base, false);
908 }
909 }
910
IpsPatchExit()911 void IpsPatchExit()
912 {
913 bDoIpsPatch = FALSE;
914 }
915