Mercurial > msvpvf
view src/gui.c @ 79:8f90d5addda9 v2.0
*: refactor... basically everything!
The Win32 GUI version is now unicode-friendly. HOWEVER, ANSI still very
much works. you can configure which version to use through `-DUNICODE=0/1`
in CFLAGS.
the CLI is also friendlier and uses a more sane interface as well.
note: the command line flags (which were optional before) are now required.
Unicode filenames will not work on Windows because Windows sucks.
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Wed, 20 Mar 2024 17:06:26 -0400 |
parents | 79a35af2cb56 |
children | c06dcab17923 |
line wrap: on
line source
/** * msvpvf GUI for Windows * Copyright (c) Paper 2022-2024 * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. **/ /* change these macros to configure the window size */ #define WINDOW_WIDTH 225 #define WINDOW_HEIGHT 200 /* mingw */ #ifdef UNICODE #define NTDDI_VERSION 0x06000000 #define _WIN32_WINNT 0x0600 #else #define _WIN32_WINNT 0x0400 #endif #include <windef.h> #include <winbase.h> #include <shlwapi.h> #include <shobjidl.h> #include <stdint.h> #include <stdio.h> #include "common.h" /* we use COM when `UNICODE=1` to avoid file paths being cut off */ #define COM_INITFLAGS (COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE) #define OPEN_FILE_BUTTON 0 #define COMBOBOX 1 #define LISTBOX 2 #define SAVE_FILE_BUTTON 3 #define VERSION 4 /* adjust for these functions for Unicode */ #if UNICODE #define _tstrncpy wcsncpy #define _tstrdup _wcsdup #define _tfopen _wfopen #define _sntprintf _snwprintf #else #define _tstrncpy strncpy #define _tstrdup _strdup #define _tfopen fopen #define _sntprintf _snprintf #endif /* enumerate over this */ static const enum types types[] = {TYPES_VF, TYPES_VEG}; static LPTSTR file_path = NULL; static uint8_t version = 13; static enum types type = TYPES_VEG; /* we edit this from display_file() */ static HWND hwnd_version = NULL; static inline LPCTSTR type_to_prefix(enum types type) { switch (type) { case TYPES_VF: return TEXT("MS"); case TYPES_VEG: return TEXT("PRO"); case TYPES_UNKNOWN: default: return TEXT("UNK"); } } static inline LPCTSTR type_to_string(enum types type) { switch (type) { case TYPES_VF: return TEXT("Movie Studio"); case TYPES_VEG: return TEXT("Vegas Pro"); case TYPES_UNKNOWN: default: return TEXT("Unknown"); } } static inline LPCTSTR type_to_extension(enum types type) { switch (type) { case TYPES_VF: return TEXT("vf"); case TYPES_VEG: case TYPES_UNKNOWN: default: return TEXT("veg"); } } /* these functions are designed to *not* use global variables, * to make everything a bit more simple... */ static int display_file(LPCTSTR path) { /* Read the file to memory */ FILE* file = _tfopen(path, TEXT("rb")); uint8_t file_version = 0; enum types file_type = TYPES_UNKNOWN; get_file_information(file, &file_version, &file_type); int needed = _sntprintf(NULL, 0, TEXT("File version: %s %u"), type_to_string(file_type), file_version); LPTSTR text = calloc(needed + 1, sizeof(TCHAR)); if (!text) /* out of memory... lol */ exit(1); _sntprintf(text, needed + 1, TEXT("File version: %s %u"), type_to_string(file_type), file_version); if (!SendMessage(hwnd_version, WM_SETTEXT, (WPARAM)0, (LPARAM)text)) { free(text); return -1; } free(text); fclose(file); return 0; } static int open_file(HWND hWnd, LPTSTR* filepath) { #if UNICODE if (CoInitializeEx(NULL, COM_INITFLAGS) != S_OK) return -1; /* what */ COMDLG_FILTERSPEC filters[] = {{L"Project files", L"*.veg;*.vf"}, {L"All files", L"*.*"}}; IFileDialog* pfd = NULL; IShellItem* result = NULL; if (SUCCEEDED(CoCreateInstance(&CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog, (LPVOID*)&pfd))) { pfd->lpVtbl->SetFileTypes(pfd, ARRAYSIZE(filters), filters); pfd->lpVtbl->SetFileTypeIndex(pfd, 1); pfd->lpVtbl->Show(pfd, hWnd); if (!SUCCEEDED(pfd->lpVtbl->GetResult(pfd, &result)) || !result) { pfd->lpVtbl->Release(pfd); return -1; } if (!SUCCEEDED(result->lpVtbl->GetDisplayName(result, SIGDN_FILESYSPATH, filepath))) { pfd->lpVtbl->Release(pfd); result->lpVtbl->Release(result); *filepath = NULL; /* might memleak? */ return -1; } result->lpVtbl->Release(result); pfd->lpVtbl->Release(pfd); } else { #endif /* initialize our buffer */ *filepath = calloc(MAX_PATH + 1, sizeof(TCHAR)); OPENFILENAME ofn = {0}; /* NT 4.0 compat */ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; ofn.hwndOwner = hWnd; ofn.lpstrFile = *filepath; ofn.nMaxFile = MAX_PATH; ofn.lpstrFilter = TEXT("Project files\0*.veg;*.vf\0All files\0*.*\0"); if (!GetOpenFileName(&ofn)) return -1; #if UNICODE } CoUninitialize(); #endif return display_file(*filepath);; } static int save_file(HWND hWnd, LPCTSTR input, uint8_t version, enum types type) { if (!input) { MessageBox(hWnd, TEXT("Please open a file first!"), TEXT("Invalid input file!"), MB_ICONEXCLAMATION); return -1; } LPTSTR output_template = NULL; LPTSTR output = NULL; { LPTSTR input_basename = PathFindFileName(input); int input_basename_len = PathFindExtension(input_basename) - input_basename; int needed = _sntprintf(NULL, 0, TEXT("%s_V%u_%.*s.%s"), type_to_prefix(type), version, input_basename_len, input_basename, type_to_extension(type)); output_template = calloc(needed + 1, sizeof(TCHAR)); _sntprintf(output_template, needed + 1, TEXT("%s_V%u_%.*s.%s"), type_to_prefix(type), version, input_basename_len, input_basename, type_to_extension(type)); free(input_basename); } { /* File dialog */ #if UNICODE int com_initialized = SUCCEEDED(CoInitializeEx(NULL, COM_INITFLAGS)); COMDLG_FILTERSPEC filters[] = {{L"Movie Studio project files", L"*.vf"}, {L"Vegas Pro project files", L"*.veg"}, {L"All files", L"*.*"}}; IFileDialog* pfd = NULL; IShellItem* result = NULL; if (com_initialized && SUCCEEDED(CoCreateInstance(&CLSID_FileSaveDialog, NULL, CLSCTX_INPROC_SERVER, &IID_IFileSaveDialog, (LPVOID*)&pfd))) { pfd->lpVtbl->SetFileTypes(pfd, ARRAYSIZE(filters), filters); pfd->lpVtbl->SetFileTypeIndex(pfd, (type == TYPES_UNKNOWN) ? ARRAYSIZE(filters) : type - TYPES_UNKNOWN); pfd->lpVtbl->SetFileName(pfd, output_template); pfd->lpVtbl->Show(pfd, hWnd); if (!SUCCEEDED(pfd->lpVtbl->GetResult(pfd, &result))) { pfd->lpVtbl->Release(pfd); free(output_template); return -1; } if (!SUCCEEDED(result->lpVtbl->GetDisplayName(result, SIGDN_FILESYSPATH, &output))) { pfd->lpVtbl->Release(pfd); result->lpVtbl->Release(result); free(output_template); output = NULL; /* might memleak ? */ return -1; } result->lpVtbl->Release(result); pfd->lpVtbl->Release(pfd); free(output_template); } else { #endif /* fallback to OPENFILENAME if COM fucks up for whatever reason (or we're on ANSI)... */ output = calloc(MAX_PATH + 1, sizeof(TCHAR)); _tstrncpy(output, output_template, MAX_PATH); free(output_template); OPENFILENAME ofn = {0}; ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; ofn.hwndOwner = hWnd; ofn.lpstrFile = output; ofn.nMaxFile = MAX_PATH; ofn.lpstrFilter = TEXT("Movie Studio project files\0*.vf\0Vegas Pro project files\0*.veg\0All files\0*.*\0"); ofn.nFilterIndex = (type == TYPES_UNKNOWN) ? 3 : type - TYPES_UNKNOWN; if (!GetSaveFileName(&ofn)) return -1; #if UNICODE } if (com_initialized) CoUninitialize(); #endif } if (!CopyFile(input, output, 0)) { MessageBox(hWnd, TEXT("Failed to copy original project file! Does the destination file already exist?"), TEXT("Saving project failed!"), MB_ICONEXCLAMATION | MB_OK); free(output); return -1; } FILE* output_file = _tfopen(output, TEXT("ab")); if (!output_file) { MessageBox(hWnd, TEXT("Failed to save project file!"), TEXT("Saving project failed!"), MB_ICONEXCLAMATION | MB_OK); free(output); return -1; } free(output); set_file_information(output_file, version, type); fclose(output_file); return 0; } /* TODO: use a resource file instead... */ void AddControls(HWND hWnd) { /* Open File */ HWND open_button = CreateWindow(TEXT("Button"), TEXT("Open"), WS_VISIBLE | WS_CHILD, WINDOW_WIDTH * 7 / 18, WINDOW_HEIGHT / 40, WINDOW_WIDTH * 2 / 9, WINDOW_HEIGHT / 10, hWnd, (HMENU)OPEN_FILE_BUTTON, NULL, NULL); /* Versions */ HWND combobox = CreateWindow(TEXT("ComboBox"), NULL, CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD | WS_VISIBLE | WS_OVERLAPPED | WS_VSCROLL, WINDOW_WIDTH * 7 / 18, WINDOW_HEIGHT * 3 / 20, WINDOW_WIDTH * 2 / 9, WINDOW_HEIGHT, hWnd, (HMENU)COMBOBOX, NULL, NULL); static LPCTSTR versions[] = {TEXT("8"), TEXT("9"), TEXT("10"), TEXT("11"), TEXT("12"), TEXT("13"), TEXT("14"), TEXT("15"), TEXT("16"), TEXT("17"), TEXT("18"), TEXT("19"), TEXT("20"), TEXT("21")}; /* wuss 9+10 */ for (size_t i = 0; i < ARRAYSIZE(versions); i++) SendMessage(combobox, (UINT)CB_ADDSTRING, (WPARAM)0, (LPARAM)versions[i]); SendMessage(combobox, CB_SETCURSEL, (WPARAM)3, (LPARAM)0); /* Type */ HWND listbox = CreateWindow(TEXT("Listbox"), NULL, WS_VISIBLE | WS_CHILD | LBS_STANDARD | LBS_NOTIFY, WINDOW_WIDTH * 5 / 18, WINDOW_HEIGHT * 11 / 40, WINDOW_WIDTH * 4 / 9, WINDOW_HEIGHT / 5, hWnd, (HMENU)LISTBOX, NULL, NULL); for (size_t i = 0; i < ARRAYSIZE(types); i++) { LRESULT pos = SendMessage(listbox, LB_ADDSTRING, i, (LPARAM)type_to_string(types[i])); SendMessage(listbox, LB_SETITEMDATA, pos, (LPARAM)types[i]); if (types[i] == type) SendMessage(listbox, LB_SETCURSEL, (WPARAM)pos, (LPARAM)0); } /* Save File */ HWND save_button = CreateWindow(TEXT("Button"), TEXT("Save"), WS_VISIBLE | WS_CHILD, WINDOW_WIDTH * 7 / 18, WINDOW_HEIGHT * 9 / 20, WINDOW_WIDTH * 2 / 9, WINDOW_HEIGHT / 10, hWnd, (HMENU)SAVE_FILE_BUTTON, NULL, NULL); /* Version and Type display */ hwnd_version = CreateWindow(TEXT("Edit"), TEXT(""), WS_VISIBLE | WS_CHILD | WS_BORDER | ES_READONLY | ES_CENTER | ES_MULTILINE | SS_CENTER, WINDOW_WIDTH / 6, WINDOW_HEIGHT * 3 / 5, WINDOW_WIDTH * 2 / 3, WINDOW_HEIGHT / 5, hWnd, (HMENU)VERSION, NULL, NULL); if (!open_button || !save_button || !listbox || !combobox || !hwnd_version) MessageBox(hWnd, TEXT("how did you even trigger this"), TEXT("GUI could not be initialized!"), MB_ICONEXCLAMATION); } int CALLBACK SetFont(HWND child, LPARAM font) { SendMessage(child, WM_SETFONT, font, 1); return 1; } LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_COMMAND: if (HIWORD(wParam) == CBN_SELCHANGE) { switch (LOWORD(wParam)) { case COMBOBOX: version = (uint8_t)(8+SendMessage((HWND)lParam, (UINT)CB_GETCURSEL, (WPARAM)0, (LPARAM)0)); break; case LISTBOX: { LRESULT i = SendMessage((HWND)lParam, (UINT)LB_GETCURSEL, (WPARAM)0, (LPARAM)0); type = (enum types)SendMessage((HWND)lParam, (UINT)LB_GETITEMDATA, (WPARAM)i, (LPARAM)0); break; } default: break; } } switch (wParam) { case OPEN_FILE_BUTTON: /* free(NULL) == no-op */ free(file_path); open_file(hWnd, &file_path); break; case SAVE_FILE_BUTTON: save_file(hWnd, file_path, version, type); break; default: break; } break; case WM_CREATE: AddControls(hWnd); EnumChildWindows(hWnd, (WNDENUMPROC)SetFont, (LPARAM)GetStockObject(DEFAULT_GUI_FONT)); break; case WM_DESTROY: PostQuitMessage(0); break; case WM_DROPFILES: { /* Drag and drop support */ free(file_path); HDROP drop = (HDROP)wParam; int needed = DragQueryFile(drop, 0, NULL, 0); file_path = malloc((needed + 1) * sizeof(TCHAR)); DragQueryFile(drop, 0, file_path, needed + 1); file_path[needed] = L'\0'; display_file(file_path); break; } default: return DefWindowProc(hWnd, msg, wParam, lParam); } return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR args, int ncmdshow) { WNDCLASS wc = {0}; wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hInstance = hInstance; wc.lpszClassName = TEXT("msvpvf"); wc.lpfnWndProc = WindowProcedure; if (!RegisterClass(&wc)) return -1; CreateWindowEx(WS_EX_ACCEPTFILES, TEXT("msvpvf"), TEXT("Movie Studio / Vegas Pro version spoofer"), WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL); MSG msg = {0}; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }