diff --git a/desmume/src/cheatSystem.cpp b/desmume/src/cheatSystem.cpp index 0a4c6731b..f7d50f19a 100644 --- a/desmume/src/cheatSystem.cpp +++ b/desmume/src/cheatSystem.cpp @@ -420,6 +420,14 @@ BOOL CHEATS::XXcodePreParser(CHEATS_LIST *list, char *code) return TRUE; } +BOOL CHEATS::add_AR_Direct(CHEATS_LIST cheat) +{ + size_t num = list.size(); + list.push_back(cheat); + list[num].type = 1; + return TRUE; +} + BOOL CHEATS::add_AR(char *code, char *description, BOOL enabled) { //if (num == MAX_CHEAT_LIST) return FALSE; @@ -1028,3 +1036,183 @@ void CHEATSEARCH::getListReset() { lastRecord = 0; } + +// ========================================================================= Export +bool CHEATSEXPORT::load(char *path) +{ + fp = fopen(path, "rb"); + if (!fp) + { + printf("Error open database\n"); + return false; + } + + fseek(fp, 0, SEEK_END); + fsize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + if (!search()) + { + printf("ERROR: cheat in database not founded\n"); + return false; + } + + if (!getCodes()) + { + printf("ERROR: export cheats failed\n"); + return false; + } + + return true; +} +void CHEATSEXPORT::close() +{ + if (fp) + fclose(fp); + if (cheats) + { + delete [] cheats; + cheats = NULL; + } +} + +// TODO: decrypting database +bool CHEATSEXPORT::search() +{ + if (!fp) return false; + + u32 pos = 0x0100; + FAT_R4 fat_empty = {0}; + + memset(&fat, 0, sizeof(FAT_R4)); + + fseek(fp, pos, SEEK_SET); + while (pos < fsize) + { + fread(&fat, sizeof(FAT_R4), 1, fp); + if (memcmp(&fat, &fat_empty, sizeof(FAT_R4)) == 0) break; + if (memcmp(gameInfo.header.gameCode, &fat.serial[0], 4) == 0) + { + FAT_R4 fat_tmp = {0}; + fread(&fat_tmp, sizeof(FAT_R4), 1, fp); + if (memcmp(&fat_tmp, &fat_empty, sizeof(FAT_R4)) == 0) + { + // TODO + dataSize = 0; + } + else + { + dataSize = (fat_tmp.addr - fat.addr); + } + char buf[5] = {0}; + memcpy(&buf, &fat.serial[0], 4); + printf("Founded %s CRC %08X at 0x%08X (size %i)\n", buf, fat.CRC, fat.addr, dataSize); + return true; + } + pos += sizeof(FAT_R4); + } + + memset(&fat, 0, sizeof(FAT_R4)); + return false; +} + +bool CHEATSEXPORT::getCodes() +{ + if (!fp) return false; + + u32 pos = 0; + u32 pos_cht = 0; + + u8 *data = new u8 [dataSize+8]; + if (!data) return false; + memset(data, 0, dataSize+8); + + fseek(fp, fat.addr, SEEK_SET); + + if (fread(data, 1, dataSize, fp) != dataSize) + { + delete [] data; + data = NULL; + return false; + } + + u8 *title = data; + u32 *cmd = (u32 *)(((u32 )title + strlen((char*)title) + 4) & 0xFFFFFFFC); + numCheats = (*cmd) & (~0xF0000000); + cmd += 9; + cheats = new CHEATS_LIST[numCheats]; + memset(cheats, 0, sizeof(CHEATS_LIST) * numCheats); + + while (pos < numCheats) + { + u32 folderNum = 1; + u8 *folderName = NULL; + u8 *folderNote = NULL; + if ((*cmd & 0xF0000000) == 0x10000000) // Folder + { + folderNum = (*cmd & 0x00FFFFFF); + folderName = (u8*)((u32)cmd + 4); + folderNote = (u8*)((u32)folderName + strlen((char*)folderName) + 1); + pos++; + numCheats--; + cmd = (u32 *)(((u32)folderName + strlen((char*)folderName) + 1 + strlen((char*)folderNote) + 1 + 3) & 0xFFFFFFFC); + } + + for (int i = 0; i < folderNum; i++) // in folder + { + u8 *cheatName = (u8 *)((u32)cmd + 4); + u8 *cheatNote = (u8 *)((u32)cheatName + strlen((char*)cheatName) + 1); + u32 *cheatData = (u32 *)(((u32)cheatNote + strlen((char*)cheatNote) + 1 + 3) & 0xFFFFFFFC); + u32 cheatDataLen = *cheatData++; + + if (cheatDataLen) + { + char buf[2048]; + memset(buf, 0, 2048); + + cheats[pos_cht].num = cheatDataLen/2; + cheats[pos_cht].type = 1; + + if ( folderName && *folderName ) + { + sprintf(cheats[pos_cht].description, "%s: ", folderName); + } + strcat(cheats[pos_cht].description, (char*)cheatName); + + if ( cheatNote && *cheatNote ) + { + strcat(cheats[pos_cht].description, "| "); + strcat(cheats[pos_cht].description, (char*)cheatNote); + } + + for(u32 j = 0, t = 0; j < (cheatDataLen/2); j++, t+=2 ) + { + cheats[pos_cht].code[j][0] = (u32)*(cheatData+t); + //printf("%i: %08X ", j, cheats[pos_cht].code[j][0]); + cheats[pos_cht].code[j][1] = (u32)*(cheatData+t+1); + //printf("%08X\n", cheats[pos_cht].code[j][1]); + + } + } + pos++; pos_cht++; + cmd = (u32 *)((u32)cmd + ((*cmd + 1)*4)); + } + + }; + + delete [] data; + + //for (int i = 0; i < numCheats; i++) + // printf("%i: %s\n", i, cheats[i].description); + + return true; +} + +CHEATS_LIST *CHEATSEXPORT::getCheats() +{ + return cheats; +} +u32 CHEATSEXPORT::getCheatsNum() +{ + return numCheats; +} \ No newline at end of file diff --git a/desmume/src/cheatSystem.h b/desmume/src/cheatSystem.h index be7c9bbb2..1ae8dea47 100644 --- a/desmume/src/cheatSystem.h +++ b/desmume/src/cheatSystem.h @@ -38,7 +38,7 @@ struct CHEATS_LIST // 2 - Codebreakers BOOL enabled; u32 code[MAX_XX_CODE][2]; - char description[75]; + char description[255]; int num; u8 size; }; @@ -68,6 +68,7 @@ public: BOOL update(u8 size, u32 address, u32 val, char *description, BOOL enabled, u32 pos); BOOL add_AR(char *code, char *description, BOOL enabled); BOOL update_AR(char *code, char *description, BOOL enabled, u32 pos); + BOOL add_AR_Direct(CHEATS_LIST cheat); BOOL add_CB(char *code, char *description, BOOL enabled); BOOL update_CB(char *code, char *description, BOOL enabled, u32 pos); BOOL remove(u32 pos); @@ -107,6 +108,53 @@ public: void getListReset(); }; +enum CHEATS_DB_TYPE +{ + CHEATS_DB_R4 = 0 +}; + +#pragma pack(push) +#pragma pack(1) + struct FAT_R4 + { + u8 serial[4]; + u32 CRC; + u64 addr; + }; +#pragma pack(pop) + +class CHEATSEXPORT +{ +private: + CHEATS_DB_TYPE type; + FILE *fp; + u32 fsize; + u32 dataSize; + FAT_R4 fat; + bool search(); + bool getCodes(); + + u32 numCheats; + CHEATS_LIST *cheats; + +public: + CHEATSEXPORT() : + fp(NULL), + fsize(0), + dataSize(0), + type(CHEATS_DB_R4), + numCheats(0), + cheats(0) + {} + ~CHEATSEXPORT() + {} + + bool load(char *path); + void close(); + CHEATS_LIST *getCheats(); + u32 getCheatsNum(); +}; + extern CHEATS *cheats; extern CHEATSEARCH *cheatSearch; diff --git a/desmume/src/windows/cheatsWin.cpp b/desmume/src/windows/cheatsWin.cpp index c0f37071c..69fc0735c 100644 --- a/desmume/src/windows/cheatsWin.cpp +++ b/desmume/src/windows/cheatsWin.cpp @@ -50,6 +50,7 @@ static u8 cheatXXaction = 0; static HWND searchWnd = NULL; static HWND searchListView = NULL; static HWND cheatListView = NULL; +static HWND exportListView = NULL; static LONG_PTR oldEditProc = NULL; static LONG_PTR oldEditProcHEX = NULL; @@ -76,6 +77,10 @@ u32 searchRange[4][2] = { { 0, 4294967295 } }; +//========================================= Export +static CHEATSEXPORT *cheatsExport = NULL; +void CheatsExportDialog(HWND hwnd); + LONG_PTR CALLBACK EditValueProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (msg == WM_CHAR) @@ -958,6 +963,10 @@ INT_PTR CALLBACK CheatsListBox_Proc(HWND dialog, UINT msg,WPARAM wparam,LPARAM l } return TRUE; + + case IDC_EXPORT: + CheatsExportDialog(dialog); + return TRUE; } break; } @@ -1372,3 +1381,107 @@ void CheatAddVerify(HWND dialog,char* addre, char* valu,u8 size) else EnableWindow(GetDlgItem(dialog, IDOK), TRUE); } + + +// ============================================================= Export +INT_PTR CALLBACK CheatsExportProc(HWND dialog, UINT msg,WPARAM wparam,LPARAM lparam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + LV_COLUMN lvColumn; + exportListView = GetDlgItem(dialog, IDC_LIST_CHEATS); + ListView_SetExtendedListViewStyle(exportListView, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES); + + memset(&lvColumn,0,sizeof(LV_COLUMN)); + lvColumn.mask=LVCF_FMT|LVCF_TEXT|LVCF_WIDTH; + lvColumn.fmt=LVCFMT_LEFT; + lvColumn.cx=1000; + lvColumn.pszText="Cheats"; + ListView_InsertColumn(exportListView, 0, &lvColumn); + + LVITEM lvi; + memset(&lvi,0,sizeof(LVITEM)); + lvi.mask = LVIF_TEXT|LVIF_STATE; + lvi.iItem = INT_MAX; + + SendMessage(exportListView, WM_SETREDRAW, (WPARAM)FALSE,0); + for (int i = 0; i < cheatsExport->getCheatsNum(); i++) + { + CHEATS_LIST *tmp = (CHEATS_LIST*)cheatsExport->getCheats(); + lvi.pszText= tmp[i].description; + SendMessage(exportListView, LVM_INSERTITEM, 0, (LPARAM)&lvi); + } + SendMessage(exportListView, WM_SETREDRAW, (WPARAM)TRUE,0); + ListView_SetItemState(exportListView,0, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED); + SetFocus(exportListView); + break; + } + case WM_COMMAND: + { + switch (LOWORD(wparam)) + { + case IDOK: + { + u32 count = ListView_GetSelectedCount(exportListView); + if (count > 0) + { + u32 prev = ListView_GetNextItem(exportListView, -1, LVIS_SELECTED); + for (int i = 0; i < count; i++) + { + CHEATS_LIST *tmp = (CHEATS_LIST*)cheatsExport->getCheats(); + cheats->add_AR_Direct(tmp[prev]); + + LVITEM lvi; + + memset(&lvi,0,sizeof(LVITEM)); + lvi.mask = LVIF_TEXT|LVIF_STATE; + lvi.iItem = INT_MAX; + lvi.pszText= " "; + u32 row = ListView_InsertItem(cheatListView, &lvi); + ListView_SetItemText(cheatListView, row, 1, "Action"); + ListView_SetItemText(cheatListView, row, 2, "Replay"); + ListView_SetItemText(cheatListView, row, 3, tmp[prev].description); + + prev = ListView_GetNextItem(exportListView, prev, LVIS_SELECTED); + } + EndDialog(dialog, TRUE); + } + } + break; + + case IDCANCEL: + EndDialog(dialog, FALSE); + break; + } + break; + } + } + return FALSE; +} + +void CheatsExportDialog(HWND hwnd) +{ + cheatsExport = new CHEATSEXPORT(); + if (!cheatsExport) return; + + // TODO: select file + if (cheatsExport->load("USRCHEAT.DAT")) + { + if (cheatsExport->getCheatsNum() > 0) + { + DialogBoxW(hAppInst, MAKEINTRESOURCEW(IDD_CHEAT_EXPORT), hwnd, (DLGPROC) CheatsExportProc); + } + else + { + MessageBox(hwnd, "Cheats for this game in database not founded.", "DeSmuME", MB_OK | MB_ICONERROR); + } + } + else + MessageBox(hwnd, "Error loading cheat database.", "DeSmuME", MB_OK | MB_ICONERROR); + + cheatsExport->close(); + delete cheatsExport; + cheatsExport = NULL; +} \ No newline at end of file diff --git a/desmume/src/windows/resource.h b/desmume/src/windows/resource.h index da38e60e1..30258c049 100644 --- a/desmume/src/windows/resource.h +++ b/desmume/src/windows/resource.h @@ -41,6 +41,7 @@ #define IDB_ROTATECW 120 #define IDM_MBG1 121 #define IDM_MBG2 122 +#define IDD_CHEAT_EXPORT 122 #define IDM_MBG3 123 #define IDM_SBG0 124 #define IDM_SBG1 125 @@ -378,7 +379,9 @@ #define IDC_PATH 1037 #define IDC_PATHGAME 1037 #define IDC_PIANO_E 1037 +#define IDC_LIST_CHEATS 1037 #define IDC_RFILE 1038 +#define IDC_EXPORT 1038 #define IDC_RFOLDER 1039 #define IDC_BBROWSE2 1040 #define IDC_PATHDESMUME 1041 @@ -932,9 +935,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 122 +#define _APS_NEXT_RESOURCE_VALUE 123 #define _APS_NEXT_COMMAND_VALUE 40079 -#define _APS_NEXT_CONTROL_VALUE 1037 +#define _APS_NEXT_CONTROL_VALUE 1039 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/desmume/src/windows/resources.rc b/desmume/src/windows/resources.rc index 198d869e5..7364a1a73 100644 Binary files a/desmume/src/windows/resources.rc and b/desmume/src/windows/resources.rc differ