#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); }
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, 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
{
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');
}
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!", "The patches were applied successfully!", "The patches were applied, but one or more may be mangled or improperly created...", "Some patches were applied, but not all of the given patches are valid...", "Some patches were applied, but not all of the desired ROMs could be created...", "Some patches were applied, but not all of the input ROMs could be read...", "Some patches were applied, but not all of the required input ROMs could be located...", "Some patches were applied, but not all of the given patches could be read...", };
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.level<el_notthis) anySuccess=true;
else canUseFoundRom=false;
}
multi_auto_next:
delete patch;
thisPatchName=wcschr(thisPatchName, '\0')+1;
}
if (anySuccess)
{
if (worsterror==e_no_auto && foundRom && canUseFoundRom && !usingFoundRom)
{
usingFoundRom=true;
goto redo;
}
int severity=(worsterror==e_none ? el_ok : el_warning);
MessageBoxA(hwndMain, messages[worsterror], flipsversion, mboxtype[severity]);
return severity;
}
}
WCHAR inromname[MAX_PATH];
inromname[0]='\0';
if (!SelectRom(inromname, TEXT("Select Base File"), false)) return 0;
WCHAR thisFileNameWithPath[MAX_PATH];
wcscpy(thisFileNameWithPath, patchnames);
LPWSTR thisFileName=wcschr(thisFileNameWithPath, '\0');
*thisFileName='\\';
thisFileName++;
LPWSTR thisPatchName=wcschr(patchnames, '\0')+1;
LPCWSTR romExtension=GetExtension(inromname);
filemap* inrommap=filemap::create(inromname);
struct mem inrom=inrommap->get();
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]={
{
NULL, NULL, NULL, "None of these are valid patches for this ROM!", "None of these are valid patches!", "Couldn't write any ROMs. Are you on a read-only medium?", "Couldn't read any patches. What exactly are you doing?", "Couldn't read the input ROM. What exactly are you doing?", },{
"The patches were applied successfully!", "The patches were applied successfully!", "The patches were applied, but one or more may be mangled or improperly created...", "Some patches were applied, but not all of the given patches are valid for this ROM...", "Some patches were applied, but not all of the given patches are valid...", "Some patches were applied, but not all of the desired ROMs could be created...", "Some patches were applied, but not all of the given patches could be read...", NULL, },
};
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)
{
if (state.enableAutoRomSelector && !anySuccess) AddToRomList(patch, inromname);
anySuccess=true;
}
delete patch;
}
else worsterror=max(worsterror, e_io_read);
thisPatchName=wcschr(thisPatchName, '\0')+1;
}
}
else worsterror=e_io_read_rom;
delete inrommap;
MessageBoxA(hwndMain, messages[anySuccess][worsterror], flipsversion, mboxtype[severity[anySuccess][worsterror]]);
return severity[anySuccess][worsterror];
#undef max
}
}
void a_CreatePatch()
{
WCHAR romnames[2][MAX_PATH];
WCHAR patchname[MAX_PATH];
romnames[0][0]='\0';
romnames[1][0]='\0';
if (!SelectRom(romnames[0], TEXT("Select ORIGINAL UNMODIFIED File to Use"), false)) return;
if (!SelectRom(romnames[1], TEXT("Select NEW MODIFIED File to Use"), false)) return;
if (!wcsicmp(romnames[0], romnames[1]))
{
MessageBoxA(hwndMain, "That's the same file! You should really use two different files.", flipsversion, mboxtype[el_broken]);
return;
}
wcscpy(patchname, romnames[1]);
LPWSTR extension=GetExtension(patchname);
if (extension) *extension='\0'; OPENFILENAME ofn;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize=sizeof(ofn);
ofn.hwndOwner=hwndMain;
ofn.lpstrFilter =
TEXT("BPS Patch File (*.bps)\0*.bps\0")
TEXT("IPS Patch File (*.ips)\0*.ips\0");
ofn.lpstrFile=patchname;
ofn.nMaxFile=MAX_PATH;
ofn.nFilterIndex=state.lastPatchType;
ofn.lpstrTitle=TEXT("Select File to Save As");
ofn.Flags=OFN_HIDEREADONLY|OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_OVERWRITEPROMPT;
ofn.lpstrDefExt=patchextensions[state.lastPatchType];
if (!GetSaveFileName(&ofn))
{
state.lastPatchType=(enum patchtype)ofn.nFilterIndex;
return;
}
state.lastPatchType=(enum patchtype)ofn.nFilterIndex;
bpsdCancel=false;
struct errorinfo errinf=CreatePatch(romnames[0], romnames[1], (enum patchtype)ofn.nFilterIndex, NULL, patchname);
if (!bpsdCancel) MessageBoxA(hwndMain, errinf.description, flipsversion, mboxtype[errinf.level]);
}
bool a_SetEmulator()
{
WCHAR newemupath[MAX_PATH];
*newemupath='\0';
OPENFILENAME ofn;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize=sizeof(ofn);
ofn.hwndOwner=hwndMain;
ofn.lpstrFilter=TEXT("Emulator Files (*.exe)\0*.exe\0All files (*.*)\0*.*\0");
ofn.lpstrFile=newemupath;
ofn.nMaxFile=MAX_PATH;
ofn.lpstrTitle=TEXT("Select Emulator to Use");
ofn.Flags=OFN_HIDEREADONLY|OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST;
ofn.lpstrDefExt=TEXT("exe");
if (!GetOpenFileName(&ofn)) return false;
set_st_emulator(newemupath);
return true;
}
int a_ApplyRun(LPCWSTR clipatchname)
{
struct mem rommem={NULL,0};
struct mem patchedmem={NULL,0};
WCHAR patchpath[MAX_PATH];
*patchpath='\0';
if (clipatchname)
{
wcscpy(patchpath, clipatchname);
}
else
{
OPENFILENAME ofn;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize=sizeof(ofn);
ofn.hwndOwner=hwndMain;
ofn.lpstrFilter=TEXT("All supported patches (*.bps, *.ips)\0*.bps;*.ips;*.ups\0All files (*.*)\0*.*\0");
ofn.lpstrFile=patchpath;
ofn.nMaxFile=MAX_PATH;
ofn.lpstrTitle=TEXT("Select Patch to Use");
ofn.Flags=OFN_HIDEREADONLY|OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST;
ofn.lpstrDefExt=TEXT("bps");
if (!GetOpenFileName(&ofn)) return 0;
}
struct errorinfo errinf;
file* patch = file::create(patchpath);
if (!patch)
{
errinf=error(el_broken, "Couldn't read input patch. What exactly are you doing?");
goto error;
}
LPCWSTR romname;
romname=NULL;
if (state.enableAutoRomSelector) romname=FindRomForPatch(patch, NULL);
WCHAR romname_base[MAX_PATH];
if (!romname)
{
romname_base[0]='\0';
if (!SelectRom(romname_base, TEXT("Select Base File"), false))
{
delete patch;
return 0;
}
romname=romname_base;
}
if (!*st_emulator && !a_SetEmulator())
{
delete patch;
return 0;
}
WCHAR outfilename_rel[MAX_PATH];
wcscpy(outfilename_rel, patchpath);
wcscpy(GetExtension(outfilename_rel), GetExtension(romname));
WCHAR outfilename[MAX_PATH];
GetFullPathName(outfilename_rel, MAX_PATH, outfilename, NULL);
errinf=ApplyPatchMem(patch, romname, true, outfilename, NULL, state.enableAutoRomSelector);
error:
if (errinf.level!=el_ok) MessageBoxA(hwndMain, errinf.description, flipsversion, mboxtype[errinf.level]);
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]);
return el_broken;
}
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, 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
}
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); 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,23, "Apply Patch"); SetActiveWindow(lastbutton);
button(104,6, 90,23, "Create Patch");
button(6, 37, 90,23, "Apply and Run");
button(104,37, 90,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()
{
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