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