diff --git a/Makefile b/Makefile index 066609669..870ea76f1 100644 --- a/Makefile +++ b/Makefile @@ -263,7 +263,8 @@ endif ifeq ($(PLATFORM),windows32) CFLAGS += -IWindows -Drandom=rand --target=x86_64-pc-windows -LDFLAGS += -lmsvcrt -lcomdlg32 -luser32 -lshell32 -lole32 -lSDL2main -Wl,/MANIFESTFILE:NUL --target=x86_64-pc-windows +LDFLAGS += -lmsvcrt -lcomdlg32 -luser32 -lshell32 -lole32 -ladvapi32 -lSDL2main -Wl,/MANIFESTFILE:NUL --target=x86_64-pc-windows + SDL_LDFLAGS := -lSDL2 GL_LDFLAGS := -lopengl32 ifneq ($(REDIST_XAUDIO),) diff --git a/SDL/configuration.h b/SDL/configuration.h index a5123e947..2dcf79f3f 100644 --- a/SDL/configuration.h +++ b/SDL/configuration.h @@ -143,7 +143,6 @@ typedef struct { bool osd; struct __attribute__((packed, aligned(4))) { - /* v0.15 */ bool allow_mouse_controls; uint8_t cgb_revision; @@ -155,6 +154,9 @@ typedef struct { char dmg_palette_name[25]; hotkey_action_t hotkey_actions[2]; uint16_t agb_revision; + + /* v1.0 */ + bool windows_associations_prompted; // Windows only }; } configuration_t; diff --git a/SDL/gui.c b/SDL/gui.c index 2edb6007d..9be19a5f4 100644 --- a/SDL/gui.c +++ b/SDL/gui.c @@ -11,6 +11,10 @@ #include "font.h" #include "audio/audio.h" +#ifdef _WIN32 +#include +#endif + static const SDL_Color gui_palette[4] = {{8, 24, 16,}, {57, 97, 57,}, {132, 165, 99}, {198, 222, 140}}; static uint32_t gui_palette_native[4]; @@ -399,15 +403,38 @@ static void return_to_root_menu(unsigned index) recalculate_menu_height(); } -static const struct menu_item options_menu[] = { +#ifdef _WIN32 +static void associate_rom_files(unsigned index); +#endif + +static +#ifndef _WIN32 +const +#endif +struct menu_item options_menu[] = { {"Emulation Options", enter_emulation_menu}, {"Graphic Options", enter_graphics_menu}, {"Audio Options", enter_audio_menu}, {"Control Options", enter_controls_menu}, +#ifdef _WIN32 + {"Associate ROM Files", associate_rom_files}, +#endif {"Back", return_to_root_menu}, {NULL,} }; +#ifdef _WIN32 +static void associate_rom_files(unsigned index) +{ + if (GB_do_windows_association()) { + options_menu[index].string = "ROM Files Associated"; + } + else { + options_menu[index].string = "Files Association Failed"; + } +} +#endif + static void enter_options_menu(unsigned index) { current_menu = options_menu; diff --git a/SDL/main.c b/SDL/main.c index 9d4eecc6a..f00267c1a 100644 --- a/SDL/main.c +++ b/SDL/main.c @@ -18,6 +18,7 @@ #include #else #include +#include #endif static bool stop_on_start = false; @@ -1253,6 +1254,36 @@ int main(int argc, char **argv) } update_viewport(); +#ifdef _WIN32 + if (!configuration.windows_associations_prompted) { + configuration.windows_associations_prompted = true; + save_configuration(); + SDL_MessageBoxButtonData buttons[2] = { + { + .flags = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, + .buttonid = 0, + .text = "No", + }, + { + .flags = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, + .buttonid = 1, + .text = "Yes", + }, + }; + SDL_MessageBoxData box = { + .title = "Associate SameBoy with Game Boy ROMs", + .message = "Would you like to associate SameBoy with Game Boy ROMs?\nThis can be also done later in the Options menu.", + .numbuttons = 2, + .buttons = buttons, + }; + int button; + SDL_ShowMessageBox(&box, &button); + if (button) { + GB_do_windows_association(); + } + } +#endif + if (filename == NULL) { stop_on_start = false; run_gui(false); diff --git a/Windows/Cartridge.ico b/Windows/Cartridge.ico new file mode 100644 index 000000000..fa76c39cc Binary files /dev/null and b/Windows/Cartridge.ico differ diff --git a/Windows/ColorCartridge.ico b/Windows/ColorCartridge.ico new file mode 100644 index 000000000..7ed8e438b Binary files /dev/null and b/Windows/ColorCartridge.ico differ diff --git a/Windows/associations.c b/Windows/associations.c new file mode 100755 index 000000000..768b8c839 --- /dev/null +++ b/Windows/associations.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include "associations.h" + +static bool set_registry_string(HKEY hive, const char *folder, const char *name, const char *value) +{ + HKEY hkey; + LONG status = RegCreateKeyExA(hive, folder, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL); + if (status != ERROR_SUCCESS || hkey == NULL) { + return false; + } + status = RegSetValueExA(hkey, name, 0, REG_SZ, (void *)value, strlen(value) + 1); + RegCloseKey(hkey); + return status == ERROR_SUCCESS; +} + +static bool delete_registry_key(HKEY hive, const char *folder, const char *name) +{ + HKEY hkey; + LONG status = RegCreateKeyExA(hive, folder, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL); + if (status != ERROR_SUCCESS || hkey == NULL) { + return false; + } + status = RegDeleteTreeA(hkey, name); + RegCloseKey(hkey); + return status == ERROR_SUCCESS; +} + +static bool set_registry_string_unicode(HKEY hive, const char *folder, const char *name, const wchar_t *value) +{ + HKEY hkey; + LONG status = RegCreateKeyExA(hive, folder, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL); + if (status != ERROR_SUCCESS || hkey == NULL) { + return false; + } + + wchar_t wide_name[strlen(name) + 1]; + MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_name, sizeof(wide_name) / sizeof(wide_name[0])); + status = RegSetValueExW(hkey, wide_name, 0, REG_SZ, (void *)value, (wcslen(value) + 1) * 2); + + RegCloseKey(hkey); + return status == ERROR_SUCCESS; +} + + +static bool associate(const char *extension, const char *class, const char *description, signed icon) +{ + char path[128] = "Software\\Classes\\"; + strcat(path, extension); + if (!set_registry_string(HKEY_CURRENT_USER, path, "", class)) return false; + + strcpy(path, "Software\\Classes\\"); + strcat(path, class); + if (!set_registry_string(HKEY_CURRENT_USER, path, "", description)) return false; + + strcat(path, "\\shell\\open\\command"); + + wchar_t exe[MAX_PATH]; + GetModuleFileNameW(NULL, exe, MAX_PATH); + + wchar_t temp[sizeof(exe) + 32]; + wsprintfW(temp, L"\"\%s\" \"%%1\"", exe); + if (!set_registry_string_unicode(HKEY_CURRENT_USER, path, "", temp)) return false; + + strcpy(path, "Software\\Classes\\"); + strcat(path, class); + strcat(path, "\\DefaultIcon"); + + wsprintfW(temp, L"\%s,%d", exe, icon); + if (!set_registry_string_unicode(HKEY_CURRENT_USER, path, "", temp)) return false; + + strcpy(path, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\"); + strcat(path, extension); + delete_registry_key(HKEY_CURRENT_USER, path, "UserChoice"); // Might not exist, do not check return value + + return true; +} + +bool GB_do_windows_association(void) +{ + bool ret = true; + ret &= associate(".gb", "SameBoy.gb", "Game Boy Game", 1); + ret &= associate(".gbc", "SameBoy.gbc", "Game Boy Color Game", 2); + ret &= associate(".isx", "SameBoy.isx", "Game Boy ISX File", 2); + + SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); + + return ret; +} diff --git a/Windows/associations.h b/Windows/associations.h new file mode 100755 index 000000000..e1d533d06 --- /dev/null +++ b/Windows/associations.h @@ -0,0 +1,2 @@ +#include +bool GB_do_windows_association(void); diff --git a/Windows/manifest.xml b/Windows/manifest.xml new file mode 100644 index 000000000..177a8c286 --- /dev/null +++ b/Windows/manifest.xml @@ -0,0 +1,22 @@ + + + + SameBoy + + + + + + diff --git a/Windows/resources.rc b/Windows/resources.rc index 7fd16edcf..75b4c00f1 100644 Binary files a/Windows/resources.rc and b/Windows/resources.rc differ