diff --git a/win32/CSaveLoadWithPreviewDlg.cpp b/win32/CSaveLoadWithPreviewDlg.cpp new file mode 100644 index 00000000..e2f1f89c --- /dev/null +++ b/win32/CSaveLoadWithPreviewDlg.cpp @@ -0,0 +1,281 @@ +/*****************************************************************************\ + Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. + This file is licensed under the Snes9x License. + For further information, consult the LICENSE file in the root directory. +\*****************************************************************************/ + +#include "CSaveLoadWithPreviewDlg.h" + +#include "wsnes9x.h" +#include "../snes9x.h" +#include "../ppu.h" +#include "../display.h" +#include +#include +#include + +CSaveLoadWithPreviewDlg::CSaveLoadWithPreviewDlg(bool is_save_dialog) +{ + for(int i = 0; i < NUM_DIALOG_SLOTS; i++) + { + previewHbmps[i] = NULL; + } + this->is_save_dialog = is_save_dialog; +} + + +CSaveLoadWithPreviewDlg::~CSaveLoadWithPreviewDlg() +{ + delete_preview_bmps(); +} + +bool CreatePreviewHbitmap(HBITMAP *hbmp) +{ + // create a HBITMAP to store the preview images in 32bit RGB + uint8_t* buffer = NULL; + BITMAPINFO *bm = (BITMAPINFO *)calloc(sizeof(BITMAPINFOHEADER), 1); + bm->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bm->bmiHeader.biWidth = PREVIEW_IMAGE_WIDTH; + bm->bmiHeader.biHeight = -PREVIEW_IMAGE_HEIGHT; // negative is top-down + bm->bmiHeader.biPlanes = 1; + bm->bmiHeader.biBitCount = 32; + bm->bmiHeader.biCompression = BI_RGB; + bm->bmiHeader.biSizeImage = 0; + *hbmp = CreateDIBSection(NULL, bm, DIB_RGB_COLORS, (void**)&buffer, 0, 0); + if(*hbmp == NULL) { + return false; + } + return true; +} + +void CSaveLoadWithPreviewDlg::delete_preview_bmps() +{ + for(int i = 0; i < NUM_DIALOG_SLOTS; i++) + { + if(previewHbmps[i]) + { + DeleteObject(previewHbmps[i]); + } + } +} + +bool CSaveLoadWithPreviewDlg::init_preview_bmps() +{ + for(int i = 0; i < NUM_DIALOG_SLOTS; i++) + { + if(!CreatePreviewHbitmap(&previewHbmps[i])) + return false; + } + return true; +} + +void CSaveLoadWithPreviewDlg::load_slot_image_text(int slot, HWND hDlg) +{ + // load the snapshot to receive the saved screenshot + FreezeUnfreezeSlot(slot, FALSE); + + // create temporary bitmap storage for screenshot, 16bit RGB + uint8_t* buffer = NULL; + BITMAPINFO *bm = (BITMAPINFO *)calloc(sizeof(BITMAPINFOHEADER) + 3 * sizeof(RGBQUAD), 1); + bm->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bm->bmiHeader.biWidth = IPPU.RenderedScreenWidth; + bm->bmiHeader.biHeight = -IPPU.RenderedScreenHeight; // negative is top-down + bm->bmiHeader.biPlanes = 1; + bm->bmiHeader.biBitCount = 16; + bm->bmiHeader.biCompression = BI_BITFIELDS; + bm->bmiHeader.biSizeImage = IPPU.RenderedScreenWidth * IPPU.RenderedScreenHeight * 2; + *(unsigned long *)&bm->bmiColors[0] = FIRST_COLOR_MASK_RGB565; + *(unsigned long *)&bm->bmiColors[1] = SECOND_COLOR_MASK_RGB565; + *(unsigned long *)&bm->bmiColors[2] = THIRD_COLOR_MASK_RGB565; + HBITMAP imageBmp = CreateDIBSection(NULL, bm, DIB_RGB_COLORS, (void**)&buffer, 0, 0); + if(imageBmp == NULL) + { + free(bm); + return; + } + + int row_bytes = IPPU.RenderedScreenWidth * 2 / 4 * 4; // DIBs always have 4-byte aligned rows + if(IPPU.RenderedScreenWidth * 2 % 4) + row_bytes += 4; + + // copy saved screenshot into temporary bitmap + uint16 *screen = GFX.Screen; + for(int h = 0; h < IPPU.RenderedScreenHeight; h++, screen += GFX.RealPPL) + { + uint16_t *row_start = (uint16_t*)(buffer + (h * row_bytes)); + for(int w = 0; w < IPPU.RenderedScreenWidth; w++) + { + row_start[w] = screen[w]; + } + } + + // strech temporary bitmap into HBIMAP for button + HDC cdc = CreateCompatibleDC(GetDC(NULL)); + HGDIOBJ old = SelectObject(cdc, previewHbmps[slot]); + int ret = StretchDIBits(cdc, 0, 0, PREVIEW_IMAGE_WIDTH, PREVIEW_IMAGE_HEIGHT, 0, 0, IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight, buffer, bm, DIB_RGB_COLORS, SRCCOPY); + SelectObject(cdc, old); + DeleteDC(cdc); + + free(bm); + + // set image to button + SendMessage(GetDlgItem(hDlg, IDC_BUTTON_SLOT_1 + slot), BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)previewHbmps[slot]); + + // text with filename and last write time + std::wstring static_text(PathFindFileName((wchar_t*)_tFromChar(Memory.ROMFilename))); + + // get file time details + char filename[_MAX_PATH + 1]; + GetSlotFilename(slot, filename); + HANDLE file_handle = CreateFile(_tFromChar(filename), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if(file_handle != INVALID_HANDLE_VALUE) + { + FILETIME ft; + SYSTEMTIME stUTC, stLocal; + // transform from file time to local time + if(GetFileTime(file_handle, NULL, NULL, &ft) && + FileTimeToSystemTime(&ft, &stUTC) && + SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal)) + { + // reserve space for date and time (both received individually) + std::vector date_string; + date_string.resize(max( + GetDateFormatEx(LOCALE_NAME_USER_DEFAULT, DATE_AUTOLAYOUT | DATE_LONGDATE, &stLocal, NULL, NULL, 0, NULL), + GetTimeFormatEx(LOCALE_NAME_USER_DEFAULT, 0, &stLocal, NULL, NULL, 0) + )); + + GetDateFormatEx(LOCALE_NAME_USER_DEFAULT, DATE_AUTOLAYOUT | DATE_LONGDATE, &stLocal, NULL, &date_string[0], 100, NULL); + static_text.append(L"\n").append(&date_string[0]); + GetTimeFormatEx(LOCALE_NAME_USER_DEFAULT, 0, &stLocal, NULL, &date_string[0], 100); + static_text.append(L" ").append(&date_string[0]); + } + } + + // set the description text + SetWindowText(GetDlgItem(hDlg, IDC_STATIC_SLOT_1 + slot), static_text.c_str()); +} + +void CSaveLoadWithPreviewDlg::init_window(HWND hDlg) +{ + if(is_save_dialog) + SetWindowText(hDlg, L"Save with preview"); + + // we need to load snapshots to receive the preview images, so we save the current state, + // load the slots, then reload the backup + std::string backup_filename = S9xGetFilename(".preview_backup", SNAPSHOT_DIR); + + // create backup + FreezeUnfreeze(backup_filename.c_str(), true); + + int x_pos = 0; + int y_pos = 0; + for(int i = 0; i < NUM_DIALOG_SLOTS; i++) + { + // second row + if(i == NUM_DIALOG_SLOTS / 2) + { + x_pos = 0; + y_pos = PREVIEW_HEIGHT + PREVIEW_TEXT_STATIC_HEIGHT; + } + + // create button and static for one slot + CreateWindow(TEXT("BUTTON"), NULL, WS_CHILDWINDOW | WS_VISIBLE | BS_BITMAP, x_pos, y_pos, PREVIEW_WIDHT, PREVIEW_HEIGHT, hDlg, (HMENU)(UINT_PTR)(IDC_BUTTON_SLOT_1 + i), GUI.hInstance, NULL); + HWND hStatic = CreateWindow(TEXT("STATIC"), TEXT(""), WS_CHILDWINDOW | WS_VISIBLE | SS_CENTER, x_pos, y_pos + PREVIEW_HEIGHT, PREVIEW_WIDHT, PREVIEW_TEXT_STATIC_HEIGHT, hDlg, (HMENU)(UINT_PTR)(IDC_STATIC_SLOT_1 + i), GUI.hInstance, NULL); + x_pos += PREVIEW_WIDHT; + + // set dialog font to static + HFONT dlg_font = (HFONT)SendMessage(hDlg, WM_GETFONT, 0, 0); + SendMessage(hStatic, WM_SETFONT, (WPARAM)dlg_font, MAKELPARAM(FALSE, 0)); + + // load one slot + load_slot_image_text(i, hDlg); + } + + // resize dialog to fit all buttons and text + int dialog_width = NUM_DIALOG_SLOTS / 2 * PREVIEW_WIDHT; + int dialog_height = 2 * (PREVIEW_HEIGHT + PREVIEW_TEXT_STATIC_HEIGHT) + 40; // +40 for cancel button + + // reposition cancel button + RECT rect_cancel, client_rect; + GetWindowRect(GetDlgItem(hDlg, IDCANCEL), &rect_cancel); + POINT topleft = { rect_cancel.left, rect_cancel.top }; + ScreenToClient(hDlg, &topleft); + GetClientRect(hDlg, &client_rect); + MoveWindow(GetDlgItem(hDlg, IDCANCEL), dialog_width - (client_rect.right - topleft.x), dialog_height - 30, rect_cancel.right - rect_cancel.left, rect_cancel.bottom - rect_cancel.top, TRUE); + + // get monitor dimensions + HMONITOR hm; + MONITORINFO mi; + hm = MonitorFromWindow(GUI.hWnd, MONITOR_DEFAULTTONEAREST); + mi.cbSize = sizeof(mi); + GetMonitorInfo(hm, &mi); + + // get title bar and borders + RECT rcMargins = { 0,0,0,0 }; + AdjustWindowRectEx(&rcMargins, GetWindowStyle(hDlg), FALSE, GetWindowExStyle(hDlg)); + rcMargins.left = abs(rcMargins.left); + rcMargins.top = abs(rcMargins.top); + + // add margins to window dimensions and position window in center of screen + dialog_height += rcMargins.top + rcMargins.bottom; + dialog_width += rcMargins.left + rcMargins.right; + int left = (mi.rcWork.right - mi.rcWork.left - dialog_width) / 2; + int top = (mi.rcWork.bottom - mi.rcWork.top - dialog_height) / 2; + SetWindowPos(hDlg, NULL, left, top, dialog_width, dialog_height, SWP_NOZORDER); + + // load backup, remove temporary state + FreezeUnfreeze(backup_filename.c_str(), false); + remove(backup_filename.c_str()); +} + +INT_PTR CALLBACK CSaveLoadWithPreviewDlg::DlgLoadWithPreview(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + CSaveLoadWithPreviewDlg* dlg = (CSaveLoadWithPreviewDlg*)GetWindowLongPtr(hDlg, GWLP_USERDATA); + switch(msg) + { + case WM_INITDIALOG: + { + SetWindowLongPtr(hDlg, GWLP_USERDATA, lParam); + dlg = (CSaveLoadWithPreviewDlg*)lParam; + dlg->init_window(hDlg); + } + return true; + case WM_COMMAND: + { + // return which button was pressed, or -1 for cancel + int id = LOWORD(wParam); + if(id >= IDC_BUTTON_SLOT_1 && id < IDC_BUTTON_SLOT_1 + NUM_DIALOG_SLOTS) + { + EndDialog(hDlg, id - IDC_BUTTON_SLOT_1); + return true; + } + else if(id == IDCANCEL) + { + EndDialog(hDlg, -1); + return true; + } + } + default: + return false; + } +} + +int CSaveLoadWithPreviewDlg::show() +{ + // disable text and confirm for the duration of the dialog + uint32 save_timeout = Settings.InitialInfoStringTimeout; + bool save_confirm = GUI.ConfirmSaveLoad; + Settings.InitialInfoStringTimeout = 0; + GUI.ConfirmSaveLoad = false; + + int ret = -1; + delete_preview_bmps(); + if(init_preview_bmps()) + { + ret = DialogBoxParam(GUI.hInstance, MAKEINTRESOURCE(IDD_DIALOG_LOAD_PREVIEW), GUI.hWnd, DlgLoadWithPreview, (LPARAM)this); + } + + GUI.ConfirmSaveLoad = save_confirm; + Settings.InitialInfoStringTimeout = save_timeout; + return ret; +} diff --git a/win32/CSaveLoadWithPreviewDlg.h b/win32/CSaveLoadWithPreviewDlg.h new file mode 100644 index 00000000..d4147c9f --- /dev/null +++ b/win32/CSaveLoadWithPreviewDlg.h @@ -0,0 +1,37 @@ +/*****************************************************************************\ + Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. + This file is licensed under the Snes9x License. + For further information, consult the LICENSE file in the root directory. +\*****************************************************************************/ + +#pragma once +#include "windows.h" + +#define NUM_DIALOG_SLOTS 10 + +#define PREVIEW_IMAGE_WIDTH SNES_WIDTH +#define PREVIEW_IMAGE_HEIGHT SNES_HEIGHT +#define PREVIEW_WIDHT (PREVIEW_IMAGE_WIDTH + 10) +#define PREVIEW_HEIGHT (PREVIEW_IMAGE_HEIGHT + 10) +#define PREVIEW_TEXT_STATIC_HEIGHT 50 + +class CSaveLoadWithPreviewDlg +{ +private: + static INT_PTR CALLBACK DlgLoadWithPreview(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + + HBITMAP previewHbmps[NUM_DIALOG_SLOTS]; + bool is_save_dialog; + + void delete_preview_bmps(); + bool init_preview_bmps(); + void load_slot_image_text(int slot, HWND hDlg); + void init_window(HWND hDlg); + +public: + CSaveLoadWithPreviewDlg(bool is_save_dialog = false); + virtual ~CSaveLoadWithPreviewDlg(); + + int show(); +}; + diff --git a/win32/rsrc/resource.h b/win32/rsrc/resource.h index 8bbce310..94c4888b 100644 --- a/win32/rsrc/resource.h +++ b/win32/rsrc/resource.h @@ -45,6 +45,7 @@ #define IDI_ICON3 161 #define IDI_ICON4 162 #define IDD_DIALOG_HACKS 164 +#define IDD_DIALOG_LOAD_PREVIEW 167 #define IDC_DRIVER 1001 #define IDC_BUFLEN 1002 #define IDC_RATE 1003 @@ -404,6 +405,8 @@ #define IDC_SFX_CLOCK_SPEED_SPIN 3036 #define IDC_NO_SPRITE_LIMIT 3037 #define IDC_SET_DEFAULTS 3038 +#define IDC_BUTTON_SLOT_1 3039 +#define IDC_STATIC_SLOT_1 3059 #define ID_FILE_EXIT 40001 #define ID_WINDOW_HIDEMENUBAR 40004 #define ID_FILE_AVI_RECORDING 40005 @@ -520,6 +523,8 @@ #define ID_INPUT_BACKGROUNDKEYBOARDHOTKEYS 40176 #define ID_INPUT_DETECTGAMEPADCHANGES 40177 #define ID_EMULATION_HACKS 40178 +#define ID_FILE_LOAD_PREVIEW 40179 +#define ID_FILE_SAVE_PREVIEW 40180 #define ID_FILE_SAVE0 44000 #define ID_FILE_SAVE1 44001 #define ID_FILE_SAVE2 44002 @@ -541,7 +546,7 @@ #define ID_FILE_LOAD7 44027 #define ID_FILE_LOAD8 44028 #define ID_FILE_LOAD9 44029 -#define ID_FILE_LOAD_OOPS 44030 +#define ID_FILE_LOAD_OOPS 44030 #define ID_FILE_LOAD_FILE 44031 #define IDM_MACSRIFLE_TOGGLE 44032 #define IDC_STATIC -1 @@ -550,9 +555,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 167 -#define _APS_NEXT_COMMAND_VALUE 40179 -#define _APS_NEXT_CONTROL_VALUE 3039 +#define _APS_NEXT_RESOURCE_VALUE 169 +#define _APS_NEXT_COMMAND_VALUE 40181 +#define _APS_NEXT_CONTROL_VALUE 3040 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/win32/rsrc/snes9x.rc b/win32/rsrc/snes9x.rc index 27bd2ecb..b35065ce 100644 --- a/win32/rsrc/snes9x.rc +++ b/win32/rsrc/snes9x.rc @@ -26,6 +26,14 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // Dialog // +IDD_DIALOG_LOAD_PREVIEW DIALOGEX 0, 0, 567, 247 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Load with Preview" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Cancel",IDCANCEL,510,226,50,14 +END + IDD_SOUND_OPTS DIALOGEX 0, 0, 414, 158 STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU CAPTION "Sound Settings" @@ -753,6 +761,13 @@ BEGIN TOPMARGIN, 7 BOTTOMMARGIN, 113 END + IDD_DIALOG_LOAD_PREVIEW, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 560 + TOPMARGIN, 7 + BOTTOMMARGIN, 240 + END END #endif // APSTUDIO_INVOKED @@ -912,6 +927,8 @@ BEGIN BEGIN MENUITEM "Dummy", ID_RECENT_DUMMY, INACTIVE END + MENUITEM "Load MultiCart...", ID_FILE_LOADMULTICART + MENUITEM SEPARATOR POPUP "&Save Game Position" BEGIN MENUITEM "Slot #&0", ID_FILE_SAVE0 @@ -944,7 +961,8 @@ BEGIN MENUITEM SEPARATOR MENUITEM "&Select File", ID_FILE_LOAD_FILE END - MENUITEM "Load MultiCart...", ID_FILE_LOADMULTICART + MENUITEM "&Save with Preview", ID_FILE_SAVE_PREVIEW + MENUITEM "&Load with Preview", ID_FILE_LOAD_PREVIEW MENUITEM SEPARATOR POPUP "Save Other" BEGIN @@ -1182,6 +1200,11 @@ BEGIN 0 END +IDD_DIALOG_LOAD_PREVIEW AFX_DIALOG_LAYOUT +BEGIN + 0 +END + ///////////////////////////////////////////////////////////////////////////// // diff --git a/win32/snes9xw.vcxproj b/win32/snes9xw.vcxproj index 8ae8d6ea..f4076502 100644 --- a/win32/snes9xw.vcxproj +++ b/win32/snes9xw.vcxproj @@ -438,6 +438,7 @@ + @@ -582,6 +583,7 @@ + diff --git a/win32/snes9xw.vcxproj.filters b/win32/snes9xw.vcxproj.filters index 6d61a587..fa98da5b 100644 --- a/win32/snes9xw.vcxproj.filters +++ b/win32/snes9xw.vcxproj.filters @@ -285,6 +285,9 @@ Filter + + GUI + @@ -626,6 +629,9 @@ Filter + + GUI + @@ -886,4 +892,4 @@ - + \ No newline at end of file diff --git a/win32/wsnes9x.cpp b/win32/wsnes9x.cpp index 1c8834d5..2424264c 100644 --- a/win32/wsnes9x.cpp +++ b/win32/wsnes9x.cpp @@ -35,6 +35,7 @@ #include "CCGShader.h" #include "../shaders/glsl.h" #include "CShaderParamDlg.h" +#include "CSaveLoadWithPreviewDlg.h" #include "../snes9x.h" #include "../memmap.h" #include "../cpuexec.h" @@ -462,9 +463,6 @@ void DoAVIOpen(const TCHAR* filename); void DoAVIClose(int reason); void RestoreGUIDisplay (); void RestoreSNESDisplay (); -void FreezeUnfreezeDialog(bool8 freeze); -void FreezeUnfreezeSlot(int slot, bool8 freeze); -void FreezeUnfreeze (const char *filename, bool8 freeze); void CheckDirectoryIsWritable (const char *filename); static void CheckMenuStates (); static void ResetFrameTimer (); @@ -2148,6 +2146,14 @@ LRESULT CALLBACK WinProc( case ID_FILE_LOAD_FILE: FreezeUnfreezeDialog(FALSE); break; + case ID_FILE_LOAD_PREVIEW: + { + CSaveLoadWithPreviewDlg dlg; + int slot = dlg.show(); + if(slot >= 0) + FreezeUnfreezeSlot(slot, FALSE); + break; + } case ID_FILE_SAVE0: FreezeUnfreezeSlot (0, TRUE); break; @@ -2181,6 +2187,14 @@ LRESULT CALLBACK WinProc( case ID_FILE_SAVE_FILE: FreezeUnfreezeDialog(TRUE); break; + case ID_FILE_SAVE_PREVIEW: + { + CSaveLoadWithPreviewDlg dlg(true); + int slot = dlg.show(); + if(slot >= 0) + FreezeUnfreezeSlot(slot, TRUE); + break; + } case ID_CHEAT_ENTER: RestoreGUIDisplay (); while (DialogBox(g_hInst, MAKEINTRESOURCE(IDD_CHEATER), hWnd, DlgCheater) == NC_SEARCHDB) @@ -3608,16 +3622,21 @@ void FreezeUnfreezeDialog(bool8 freeze) } } +void GetSlotFilename(int slot, char filename[_MAX_PATH + 1]) +{ + char ext[_MAX_EXT + 1]; + + if(slot == -1) + strcpy(ext, ".oops"); + else + snprintf(ext, _MAX_EXT, ".%03d", slot); + strcpy(filename, S9xGetFilename(ext, SNAPSHOT_DIR)); +} + void FreezeUnfreezeSlot(int slot, bool8 freeze) { char filename[_MAX_PATH + 1]; - char ext[_MAX_EXT + 1]; - - if (slot == -1) - strcpy(ext, ".oops"); - else - snprintf(ext, _MAX_EXT, ".%03d", slot); - strcpy(filename, S9xGetFilename(ext, SNAPSHOT_DIR)); + GetSlotFilename(slot, filename); FreezeUnfreeze(filename, freeze); } @@ -3743,6 +3762,9 @@ static void CheckMenuStates () for (int i = ID_FILE_LOAD0; i <= ID_FILE_LOAD_FILE; i++) SetMenuItemInfo(GUI.hMenu, i, FALSE, &mii); + SetMenuItemInfo(GUI.hMenu, ID_FILE_SAVE_PREVIEW, FALSE, &mii); + SetMenuItemInfo(GUI.hMenu, ID_FILE_LOAD_PREVIEW, FALSE, &mii); + SetMenuItemInfo (GUI.hMenu, ID_FILE_RESET, FALSE, &mii); SetMenuItemInfo (GUI.hMenu, ID_CHEAT_ENTER, FALSE, &mii); SetMenuItemInfo (GUI.hMenu, ID_CHEAT_SEARCH_MODAL, FALSE, &mii); diff --git a/win32/wsnes9x.h b/win32/wsnes9x.h index cb09e3cf..199ddd2e 100644 --- a/win32/wsnes9x.h +++ b/win32/wsnes9x.h @@ -26,6 +26,7 @@ #include #endif #include "rsrc/resource.h" +#include "port.h" #define COUNT(a) (sizeof (a) / sizeof (a[0])) #define MAX_AUDIO_NAME_LENGTH 1024 @@ -458,5 +459,9 @@ int GetFilterScale(RenderFilter filterID); bool GetFilterHiResSupport(RenderFilter filterID); const TCHAR * S9xGetDirectoryT (enum s9x_getdirtype); RECT GetWindowMargins(HWND hwnd, UINT width); +void GetSlotFilename(int slot, char filename[_MAX_PATH + 1]); +void FreezeUnfreezeSlot(int slot, bool8 freeze); +void FreezeUnfreezeDialog(bool8 freeze); +void FreezeUnfreeze(const char *filename, bool8 freeze); #endif // !defined(SNES9X_H_INCLUDED)