//Module name: Floating IPS, Windows frontend //Author: Alcaro //Date: See Git history //Licence: GPL v3.0 or higher #include "flips.h" #ifdef FLIPS_WINDOWS class file_w32 : public file { size_t size; HANDLE io; public: static file* create(LPCWSTR filename) { HANDLE io = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (io==INVALID_HANDLE_VALUE) return NULL; return new file_w32(io, (size_t)0); } private: file_w32(HANDLE io, uint32_t sizetsize) : io(io) { size = GetFileSize(io, NULL); } file_w32(HANDLE io, uint64_t sizetsize) : io(io) { GetFileSizeEx(io, (PLARGE_INTEGER)&size); } public: size_t len() { return size; } bool read(uint8_t* target, size_t start, size_t len) { OVERLAPPED ov = {0}; ov.Offset = start; ov.OffsetHigh = start>>16>>16; DWORD actuallen; return (ReadFile(io, target, len, &actuallen, &ov) && len==actuallen); } ~file_w32() { CloseHandle(io); } }; file* file::create(LPCWSTR filename) { return file_w32::create(filename); } class filewrite_w32 : public filewrite { HANDLE io; public: static filewrite* create(LPCWSTR filename) { HANDLE io = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (!io) return NULL; return new filewrite_w32(io); } private: filewrite_w32(HANDLE io) : io(io) {} public: bool append(const uint8_t* data, size_t len) { DWORD truelen; return (WriteFile(io, data, len, &truelen, NULL) && truelen==len); } ~filewrite_w32() { CloseHandle(io); } }; filewrite* filewrite::create(LPCWSTR filename) { return filewrite_w32::create(filename); } //TODO: implement properly //also ensure input==output works if implementing this, rather than getting a file sharing violation //applies even when selecting multiple patches, of which one overwrites input filemap* filemap::create(LPCWSTR filename) { return filemap::create_fallback(filename); } HWND hwndMain=NULL; HWND hwndSettings=NULL; struct { char signature[9]; unsigned char cfgversion; unsigned char lastRomType; bool openInEmulatorOnAssoc; bool enableAutoRomSelector; enum patchtype lastPatchType; int windowleft; int windowtop; } static state; #define mycfgversion 2 WCHAR * st_emulator=NULL; void set_st_emulator_len(LPCWSTR newemu, int len) { free(st_emulator); st_emulator=(WCHAR*)malloc((len+1)*sizeof(WCHAR)); if (newemu) memcpy(st_emulator, newemu, len*sizeof(WCHAR)); st_emulator[len]='\0'; } void set_st_emulator(LPCWSTR newemu) { set_st_emulator_len(newemu, wcslen(newemu)); } HWND hwndProgress; LRESULT CALLBACK bpsdProgressWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); bool bpsdCancel; void bpsdeltaBegin() { bpsdCancel=false; RECT mainwndpos; GetWindowRect(hwndMain, &mainwndpos); hwndProgress=CreateWindowA( "floatingmunchers", flipsversion, WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_BORDER, mainwndpos.left+53, mainwndpos.top+27, 101, 39, hwndMain, NULL, GetModuleHandle(NULL), NULL); SetWindowLongPtrA(hwndProgress, GWLP_WNDPROC, (LONG_PTR)bpsdProgressWndProc); ShowWindow(hwndProgress, SW_SHOW); EnableWindow(hwndMain, FALSE); bpsdeltaProgress(NULL, 0, 1); } bool bpsdeltaProgress(void* userdata, size_t done, size_t total) { if (!bpsdeltaGetProgress(done, total)) return !bpsdCancel; if (hwndProgress) InvalidateRect(hwndProgress, NULL, false); MSG Msg; while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) DispatchMessage(&Msg); return !bpsdCancel; } LRESULT CALLBACK bpsdProgressWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_ERASEBKGND: return TRUE; case WM_PAINT: { PAINTSTRUCT ps; RECT rc; BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rc); FillRect(ps.hdc, &rc, GetSysColorBrush(COLOR_3DFACE)); SetBkColor(ps.hdc, GetSysColor(COLOR_3DFACE)); SelectObject(ps.hdc, (HFONT)GetStockObject(DEFAULT_GUI_FONT)); DrawTextA(ps.hdc, bpsdProgStr, -1, &rc, DT_CENTER | DT_NOCLIP); EndPaint(hwnd, &ps); } break; case WM_CLOSE: bpsdCancel=true; break; default: return DefWindowProcA(hwnd, uMsg, wParam, lParam); } return 0; } void bpsdeltaEnd() { EnableWindow(hwndMain, TRUE); DestroyWindow(hwndProgress); hwndProgress=NULL; } bool SelectRom(LPWSTR filename, LPCWSTR title, bool output) { OPENFILENAME ofn; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize=sizeof(ofn); ofn.hwndOwner=hwndMain; ofn.lpstrFilter=TEXT("Most Common ROM Files\0*.smc;*.sfc;*.nes;*.gb;*.gbc;*.gba;*.vb;*.sms;*.smd;*.md;*.ngp;*.n64;*.z64\0All Files (*.*)\0*.*\0"); ofn.lpstrFile=filename; ofn.nMaxFile=MAX_PATH; ofn.nFilterIndex=state.lastRomType; ofn.lpstrTitle=title; ofn.Flags=OFN_PATHMUSTEXIST|(output?OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT:OFN_FILEMUSTEXIST); ofn.lpstrDefExt=TEXT("smc"); if (!output && !GetOpenFileName(&ofn)) return false; if ( output && !GetSaveFileName(&ofn)) return false; state.lastRomType=ofn.nFilterIndex; return true; } UINT mboxtype[]={ MB_OK, MB_OK, MB_OK|MB_ICONWARNING, MB_OK|MB_ICONWARNING, MB_OK|MB_ICONERROR, MB_OK|MB_ICONERROR }; LPCWSTR patchextensions[]={ NULL,//unused, ty_null TEXT("bps"), TEXT("ips"), }; static struct errorinfo error(errorlevel level, const char * text) { struct errorinfo errinf = { level, text }; return errinf; } int a_ApplyPatch(LPCWSTR clipatchname) { WCHAR patchnames[65536]; patchnames[0]='\0'; bool multiplePatches; if (clipatchname) { multiplePatches=false; wcscpy(patchnames, clipatchname); } else { //get patch names OPENFILENAME ofn; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize=sizeof(ofn); ofn.hwndOwner=hwndMain; ofn.lpstrFilter=TEXT("All supported patches (*.ips, *.bps)\0*.ips;*.bps;*.ups\0All files (*.*)\0*.*\0"); ofn.lpstrFile=patchnames; ofn.nMaxFile=65535; ofn.lpstrTitle=TEXT("Select Patches to Use"); ofn.Flags=OFN_HIDEREADONLY|OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_ALLOWMULTISELECT|OFN_EXPLORER; ofn.lpstrDefExt=patchextensions[state.lastPatchType]; if (!GetOpenFileName(&ofn)) return 0; multiplePatches=(ofn.nFileOffset && patchnames[ofn.nFileOffset-1]=='\0'); } //get rom name and apply if (!multiplePatches) { file* patch = file::create(patchnames); if (patch) { WCHAR inromname_buf[MAX_PATH]; LPCWSTR inromname=NULL; if (state.enableAutoRomSelector) inromname=FindRomForPatch(patch, NULL); if (!inromname) { inromname=inromname_buf; inromname_buf[0]='\0'; if (!SelectRom(inromname_buf, TEXT("Select File to Patch"), false)) goto cancel; } WCHAR outromname[MAX_PATH]; wcscpy(outromname, inromname); LPWSTR outrompath=GetBaseName(outromname); LPWSTR patchpath=GetBaseName(patchnames); if (outrompath && patchpath) { wcscpy(outrompath, patchpath); LPWSTR outromext=GetExtension(outrompath); LPWSTR inromext=GetExtension(inromname); if (*inromext && *outromext) wcscpy(outromext, inromext); } if (!SelectRom(outromname, TEXT("Select Output File"), true)) goto cancel; struct errorinfo errinf=ApplyPatchMem(patch, inromname, true, outromname, NULL, state.enableAutoRomSelector); delete patch; MessageBoxA(hwndMain, errinf.description, flipsversion, mboxtype[errinf.level]); return errinf.level; } cancel: delete patch; return 0; } else { #define max(a, b) (a > b ? a : b) if (state.enableAutoRomSelector) { LPCWSTR foundRom=NULL; bool canUseFoundRom=true; bool usingFoundRom=false; redo: ; WCHAR thisFileNameWithPath[MAX_PATH]; bool anySuccess=false; enum { e_none, e_notice, e_warning, e_invalid, e_io_rom_write, e_io_rom_read, e_no_auto, e_io_read_patch } worsterror=e_none; LPCSTR messages[8]={ "The patches were applied successfully!",//e_none "The patches were applied successfully!",//e_notice (ignore) "The patches were applied, but one or more may be mangled or improperly created...",//e_warning "Some patches were applied, but not all of the given patches are valid...",//e_invalid "Some patches were applied, but not all of the desired ROMs could be created...",//e_rom_io_write "Some patches were applied, but not all of the input ROMs could be read...",//e_io_rom_read "Some patches were applied, but not all of the required input ROMs could be located...",//e_no_auto "Some patches were applied, but not all of the given patches could be read...",//e_io_read_patch }; wcscpy(thisFileNameWithPath, patchnames); LPWSTR thisFileName=wcschr(thisFileNameWithPath, '\0'); *thisFileName='\\'; thisFileName++; LPWSTR thisPatchName=wcschr(patchnames, '\0')+1; while (*thisPatchName) { wcscpy(thisFileName, thisPatchName); file* patch = file::create(thisFileNameWithPath); { if (!patch) { worsterror=max(worsterror, e_io_read_patch); canUseFoundRom=false; goto multi_auto_next; } bool possible; LPCWSTR romname=FindRomForPatch(patch, &possible); if (usingFoundRom) { if (!romname) romname=foundRom; else goto multi_auto_next; } else { if (!romname) { if (possible) canUseFoundRom=false; worsterror=max(worsterror, e_no_auto); goto multi_auto_next; } } if (!foundRom) foundRom=romname; if (foundRom!=romname) canUseFoundRom=false; wcscpy(GetExtension(thisFileName), GetExtension(romname)); struct errorinfo errinf=ApplyPatchMem(patch, romname, true, thisFileNameWithPath, NULL, true); if (errinf.level==el_broken) worsterror=max(worsterror, e_invalid); if (errinf.level==el_notthis) worsterror=max(worsterror, e_no_auto); if (errinf.level==el_warning) worsterror=max(worsterror, e_warning); if (errinf.levelget(); bool anySuccess=false; enum { e_none, e_notice, e_warning, e_invalid_this, e_invalid, e_io_write, e_io_read, e_io_read_rom } worsterror=e_none; enum errorlevel severity[2][8]={ { el_ok, el_ok, el_warning,el_broken, el_broken, el_broken, el_broken, el_broken }, { el_ok, el_ok, el_warning,el_warning, el_warning, el_warning,el_warning,el_broken }, }; LPCSTR messages[2][8]={ { //no error-free NULL,//e_none NULL,//e_notice NULL,//e_warning "None of these are valid patches for this ROM!",//e_invalid_this "None of these are valid patches!",//e_invalid "Couldn't write any ROMs. Are you on a read-only medium?",//e_io_write "Couldn't read any patches. What exactly are you doing?",//e_io_read "Couldn't read the input ROM. What exactly are you doing?",//e_io_read_rom },{ //at least one error-free "The patches were applied successfully!",//e_none "The patches were applied successfully!",//e_notice "The patches were applied, but one or more may be mangled or improperly created...",//e_warning "Some patches were applied, but not all of the given patches are valid for this ROM...",//e_invalid_this "Some patches were applied, but not all of the given patches are valid...",//e_invalid "Some patches were applied, but not all of the desired ROMs could be created...",//e_io_write "Some patches were applied, but not all of the given patches could be read...",//e_io_read NULL,//e_io_read_rom }, }; if (inrom.ptr) { bool removeheaders=shouldRemoveHeader(inromname, inrom.len); while (*thisPatchName) { wcscpy(thisFileName, thisPatchName); file* patch = file::create(thisFileNameWithPath); if (patch) { LPWSTR patchExtension=GetExtension(thisFileName); wcscpy(patchExtension, romExtension); struct errorinfo errinf=ApplyPatchMem2(patch, inrom, removeheaders, true, thisFileNameWithPath, NULL); if (errinf.level==el_broken) worsterror=max(worsterror, e_invalid); if (errinf.level==el_notthis) worsterror=max(worsterror, e_invalid_this); if (errinf.level==el_warning) worsterror=max(worsterror, e_warning); if (errinf.level=el_notthis) return el_broken; delete patch; if (rommem.ptr) FreeFileMemory(rommem); if (patchedmem.ptr) free(patchedmem.ptr); WCHAR cmdline[1+MAX_PATH+3+MAX_PATH+1+1]; swprintf(cmdline, 1+MAX_PATH+3+MAX_PATH+1+1, TEXT("\"%ls\" \"%ls\""), st_emulator, outfilename); WCHAR * dirend=GetBaseName(patchpath); if (dirend) *dirend='\0'; STARTUPINFO startupinfo; ZeroMemory(&startupinfo, sizeof(STARTUPINFO)); PROCESS_INFORMATION processinformation; if (!CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, patchpath, &startupinfo, &processinformation)) { MessageBoxA(hwndMain, "Couldn't open emulator.", flipsversion, mboxtype[el_broken]); //DeleteFile(tempfilename); return el_broken; } //I don't clean up the temp file when the emulator is done. //- It would just force me to keep track of a bunch of state. //- It'd force me to not exit when the window is closed. //- The bsnes profile selector would confuse it. //- The emulator may have created a bunch of other files, for example SRAM. //Few other apps clean up anyways. CloseHandle(processinformation.hProcess); CloseHandle(processinformation.hThread); return errinf.level; } void a_AssignFileTypes(bool checkonly); HWND assocText; HWND assocButton; void a_ShowSettings() { if (hwndSettings) { SetActiveWindow(hwndSettings); return; } hwndSettings=CreateWindowA( "floatingmunchers", flipsversion, WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_BORDER|WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, 3+6+202+6+3, 21 + 6+23+6+23+3+13+1+17+4+17+6 + 3, NULL, NULL, GetModuleHandle(NULL), NULL); HFONT hfont=(HFONT)GetStockObject(DEFAULT_GUI_FONT); HWND item; int x=6; int y=6; int lineheight; #define endline(padding) do { x=6; y+=lineheight+padding; } while(0) #define line(height) lineheight=height #define widget(type, style, text, w, h, action) \ do { \ int thisy=y+(lineheight-h)/2; \ item=CreateWindowA(type, text, WS_CHILD|WS_TABSTOP|WS_VISIBLE|style, \ x, thisy, w, h, hwndSettings, (HMENU)(action), GetModuleHandle(NULL), NULL); \ SendMessage(item, WM_SETFONT, (WPARAM)hfont, 0); \ x+=w+6; \ } while(0) #define firstbutton(text, w, h, action) \ widget(WC_BUTTONA, WS_GROUP|BS_DEFPUSHBUTTON, text, w, h, action) #define button(text, w, h, action) \ widget(WC_BUTTONA, BS_PUSHBUTTON, text, w, h, action) #define labelL(text, w, h, action) \ widget(WC_STATICA, SS_LEFT, text, w, h, action) #define labelC(text, w, h, action) \ widget(WC_STATICA, SS_CENTER, text, w, h, action) #define radio(text, w, h, action) \ widget(WC_BUTTONA, BS_AUTORADIOBUTTON, text, w, h, action) #define check(text, w, h, action) \ widget(WC_BUTTONA, BS_AUTOCHECKBOX, text, w, h, action) line(23); firstbutton("Select emulator", 202/*94*/, 23, 101); endline(6); line(23); button("Assign file types", 98, 23, 102); assocButton=item; labelL("(can not be undone)", 98, 13, 0); assocText=item; endline(3); line(13); labelC("When opening through associations:", 175, 13, 0); endline(1); line(17); radio("Create ROM", 79, 17, 103); Button_SetCheck(item, (state.openInEmulatorOnAssoc==false)); radio("Run in emulator", 95, 17, 104); Button_SetCheck(item, (state.openInEmulatorOnAssoc==true)); endline(4); line(17); check("Enable automatic ROM selector", 202, 17, 105); Button_SetCheck(item, (state.enableAutoRomSelector)); endline(3); ShowWindow(hwndSettings, SW_SHOW); #undef firstbutton #undef button #undef label #undef radio //if (!fileTypesAssigned) button(6,68,200,23, "Assign File Types"); } void key_core(bool checkonly, LPCWSTR path, LPCWSTR value, bool * p_hasExts, bool * p_refresh) { HKEY hkey; DWORD type; WCHAR truepath[60]; wcscpy(truepath, TEXT("Software\\Classes\\")); wcscat(truepath, path); WCHAR regval[MAX_PATH+30]; DWORD regvallen; bool hasExts=true; if (checkonly) { regvallen=sizeof(regval); if (RegOpenKeyEx(HKEY_CURRENT_USER, truepath, 0, KEY_READ, &hkey)!=ERROR_SUCCESS) goto add; if (value && RegQueryValueEx(hkey, NULL, NULL, &type, (LPBYTE)regval, ®vallen)!=ERROR_SUCCESS) hasExts=false; RegCloseKey(hkey); if (!hasExts) goto add; if (value && wcsncmp(regval, value, sizeof(regval)/sizeof(*regval))) *p_hasExts=false; return; } else { add: regvallen=sizeof(regval); if (RegCreateKeyExW(HKEY_CURRENT_USER, truepath, 0, NULL, 0, KEY_WRITE, NULL, &hkey, NULL)==ERROR_SUCCESS) { if (value) RegSetValueExW(hkey, NULL, 0, REG_SZ, (BYTE*)value, (wcslen(value)+1)*sizeof(WCHAR)); RegCloseKey(hkey); } if (path[0]=='.') *p_refresh=true; return; } } void a_AssignFileTypes(bool checkonly) { WCHAR outstring[MAX_PATH+30]; outstring[0]='"'; GetModuleFileNameW(NULL, outstring+1, MAX_PATH); LPWSTR outstringend=wcschr(outstring, '\0'); *outstringend='"'; outstringend++; bool hasExts=true; bool refresh=false; #define key(path, value) \ key_core(checkonly, TEXT(path), TEXT(value), &hasExts, &refresh) #define key_path(path, value) \ wcscpy(outstringend, TEXT(value)); key_core(checkonly, TEXT(path), outstring, &hasExts, &refresh) #define key_touch(path) \ key_core(checkonly, TEXT(path), NULL, &hasExts, &refresh) key(".ips", "FloatingIPSFileIPS"); key("FloatingIPSFileIPS", "Floating IPS File"); key_path("FloatingIPSFileIPS\\DefaultIcon", ",1"); key_touch("FloatingIPSFileIPS\\shell"); key_touch("FloatingIPSFileIPS\\shell\\open"); key_path("FloatingIPSFileIPS\\shell\\open\\command", " \"%1\""); key(".bps", "FloatingIPSFileBPS"); key("FloatingIPSFileBPS", "Floating IPS File"); key_path("FloatingIPSFileBPS\\DefaultIcon", ",2"); key_touch("FloatingIPSFileBPS\\shell"); key_touch("FloatingIPSFileBPS\\shell\\open"); key_path("FloatingIPSFileBPS\\shell\\open\\command", " \"%1\""); if (refresh) { SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); if (hwndMain) { RECT wndpos; GetWindowRect(hwndMain, &wndpos); MoveWindow(hwndMain, wndpos.left, wndpos.top, 218, 93, true); } } if (!checkonly || hasExts) { SetWindowText(assocText, TEXT("(already done)")); Button_Enable(assocButton, false); } } LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_COMMAND: { if (wParam==1) a_ApplyPatch(NULL); if (wParam==2) a_CreatePatch(); if (wParam==3) a_ApplyRun(NULL); if (wParam==4) a_ShowSettings(); if (wParam==101) a_SetEmulator(); if (wParam==102) a_AssignFileTypes(false); if (wParam==103) state.openInEmulatorOnAssoc=false; if (wParam==104) state.openInEmulatorOnAssoc=true; if (wParam==105) state.enableAutoRomSelector^=1; } break; case WM_CLOSE: { if (hwnd==hwndMain && !IsIconic(hwnd)) { RECT wndpos; GetWindowRect(hwnd, &wndpos); state.windowleft=wndpos.left; state.windowtop=wndpos.top; } DestroyWindow(hwnd); } break; case WM_DESTROY: { if (hwnd==hwndMain) PostQuitMessage(0); if (hwnd==hwndSettings) hwndSettings=NULL; break; } default: return DefWindowProcA(hwnd, uMsg, wParam, lParam); } return 0; } static HFONT try_create_font(const char * name, int size) { return CreateFontA(-size*96/72, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, name); } int ShowMainWindow(HINSTANCE hInstance, int nCmdShow) { WNDCLASSA wc; wc.style=0; wc.lpfnWndProc=WindowProc; wc.cbClsExtra=0; wc.cbWndExtra=0; wc.hInstance=GetModuleHandle(NULL); wc.hIcon=LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(0)); wc.hCursor=LoadCursor(NULL, IDC_ARROW); wc.hbrBackground=GetSysColorBrush(COLOR_3DFACE);//(HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName=NULL; wc.lpszClassName="floatingmunchers"; RegisterClassA(&wc); MSG msg; hwndMain=CreateWindowA( "floatingmunchers", flipsversion, WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_BORDER|WS_MINIMIZEBOX, state.windowleft, state.windowtop, 204, 93, NULL, NULL, GetModuleHandle(NULL), NULL); HFONT hfont=try_create_font("Segoe UI", 9); if (!hfont) hfont=try_create_font("MS Shell Dlg 2", 8); if (!hfont) hfont=(HFONT)GetStockObject(DEFAULT_GUI_FONT); int buttonid=0; HWND lastbutton; #define button(x,y,w,h, text) \ do { \ lastbutton=CreateWindowA("BUTTON", text, WS_CHILD|WS_TABSTOP|WS_VISIBLE|(buttonid==0?(BS_DEFPUSHBUTTON|WS_GROUP):(BS_PUSHBUTTON)), \ x, y, w, h, hwndMain, (HMENU)(uintptr_t)(buttonid+1), GetModuleHandle(NULL), NULL); \ SendMessage(lastbutton, WM_SETFONT, (WPARAM)hfont, 0); \ buttonid++; \ } while(0) button(6, 6, 90/*77*/,23, "Apply Patch"); SetActiveWindow(lastbutton); button(104,6, 90/*83*/,23, "Create Patch"); button(6, 37, 90/*90*/,23, "Apply and Run"); button(104,37, 90/*59*/,23, "Settings"); ShowWindow(hwndMain, nCmdShow); a_AssignFileTypes(true); while (GetMessageA(&msg, NULL, 0, 0)>0) { if (!IsDialogMessageA(hwndMain, &msg)) { TranslateMessage(&msg); DispatchMessageA(&msg); } } return msg.wParam; } void GUIClaimConsole() { //this one makes it act like a console app in all cases except it doesn't create a new console if // not launched from one (it'd swiftly go away on app exit anyways), and it doesn't like being // launched from cmd since cmd wants to run a new command if spawning a gui app (I can't make it // not be a gui app because that flashes a console; it acts sanely from batch files) bool claimstdin=(GetFileType(GetStdHandle(STD_INPUT_HANDLE))==FILE_TYPE_UNKNOWN); bool claimstdout=(GetFileType(GetStdHandle(STD_OUTPUT_HANDLE))==FILE_TYPE_UNKNOWN); bool claimstderr=(GetFileType(GetStdHandle(STD_ERROR_HANDLE))==FILE_TYPE_UNKNOWN); if (claimstdin || claimstdout || claimstderr) AttachConsole(ATTACH_PARENT_PROCESS); if (claimstdin) freopen("CONIN$", "rt", stdin); if (claimstdout) freopen("CONOUT$", "wt", stdout); if (claimstderr) freopen("CONOUT$", "wt", stderr); if (claimstdout) fputc('\r', stdout); if (claimstderr) fputc('\r', stderr); } HINSTANCE hInstance_; int nCmdShow_; WCHAR * get_cfgpath() { static WCHAR cfgfname[MAX_PATH+8]; GetModuleFileNameW(NULL, cfgfname, MAX_PATH); WCHAR * ext=GetExtension(cfgfname); if (ext) *ext='\0'; wcscat(cfgfname, TEXT("cfg.bin")); return cfgfname; } void GUILoadConfig() { memset(&state, 0, sizeof(state)); struct mem configbin=ReadWholeFile(get_cfgpath()); void* configbin_org=configbin.ptr; if (configbin.len >= sizeof(state)) { #define readconfig(target, size) \ if (size<0 || configbin.len < size) goto badconfig; \ memcpy(target, configbin.ptr, size); \ configbin.ptr += size; \ configbin.len -= size readconfig(&state, sizeof(state)); if (memcmp(state.signature, "FlipscfgW", sizeof(state.signature))!=0 || state.cfgversion!=mycfgversion) goto badconfig; int emulen; readconfig(&emulen, sizeof(emulen)); set_st_emulator_len(NULL, emulen); readconfig(st_emulator, (unsigned)emulen*sizeof(WCHAR)); SetRomList(configbin); } else { badconfig: memcpy(state.signature, "FlipscfgW", sizeof(state.signature)); state.cfgversion=mycfgversion; state.lastRomType=0; state.openInEmulatorOnAssoc=false; state.enableAutoRomSelector=false; state.lastPatchType=ty_bps; state.windowleft=CW_USEDEFAULT; state.windowtop=CW_USEDEFAULT; set_st_emulator(TEXT("")); } free(configbin_org); } int GUIShow(LPCWSTR filename) { GUILoadConfig(); INITCOMMONCONTROLSEX initctrls; initctrls.dwSize=sizeof(initctrls); initctrls.dwICC=ICC_STANDARD_CLASSES; InitCommonControlsEx(&initctrls); int ret; if (filename) { if (state.openInEmulatorOnAssoc==false) ret=a_ApplyPatch(filename); else ret=a_ApplyRun(filename); } else ret=ShowMainWindow(hInstance_, nCmdShow_); HANDLE file=CreateFile(get_cfgpath(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL); if (file!=INVALID_HANDLE_VALUE) { DWORD whocares; WriteFile(file, &state, sizeof(state), &whocares, NULL); int len=wcslen(st_emulator); WriteFile(file, &len, sizeof(len), &whocares, NULL); WriteFile(file, st_emulator, sizeof(WCHAR)*wcslen(st_emulator), &whocares, NULL); struct mem romlist=GetRomList(); WriteFile(file, romlist.ptr, romlist.len, &whocares, NULL); CloseHandle(file); } return ret; } int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { hInstance_=hInstance; nCmdShow_=nCmdShow; int argc; wchar_t ** argv=CommandLineToArgvW(GetCommandLineW(), &argc); return flipsmain(argc, argv); } #endif