# HG changeset patch # User Paper # Date 1710968786 14400 # Node ID 8f90d5addda9156e57252b4afa2dfd7ed1a018f7 # Parent fae1d67d8cfd03c113005c4a5cd47ebbf248bd70 *: 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. diff -r fae1d67d8cfd -r 8f90d5addda9 Makefile --- a/Makefile Sun Oct 01 03:03:29 2023 -0400 +++ b/Makefile Wed Mar 20 17:06:26 2024 -0400 @@ -1,21 +1,16 @@ -CFLAGS=-Wall -O2 -fstack-protector -fdata-sections -ffunction-sections -LDFLAGS= +_CFLAGS = -Wall -O2 -Iinclude $(CFLAGS) +_LDFLAGS = $(LDFLAGS) -ifeq ($(shell uname -s),Darwin) # macOS is the odd one... - LDFLAGS+=-Wl,-dead_strip -else - LDFLAGS+=-Wl,--gc-sections -endif +.c.o: + $(CC) -c $(_CFLAGS) $< -o $@ -src/%.o : src/%.c - $(CC) -c $(CFLAGS) $< -o $@ - +# this is a unix-like version, barely functional on windows. msvpvf: src/main.o src/common.o - $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + $(CC) $(_CFLAGS) -o $@ $^ $(_LDFLAGS) -# GUI is windows-only, please use cross-compiler! +# windows-specific gui: src/gui.o src/common.o - $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -mwindows -lshlwapi + $(CC) -o $@ $^ -mwindows -lole32 -lshlwapi -luuid -lmsvcrt $(_LDFLAGS) clean: - rm -f src/*.o *.exe msvpvf gui + rm -f src/main.o src/common.o src/gui.o msvpvf gui diff -r fae1d67d8cfd -r 8f90d5addda9 include/common.h --- a/include/common.h Sun Oct 01 03:03:29 2023 -0400 +++ b/include/common.h Wed Mar 20 17:06:26 2024 -0400 @@ -1,2 +1,21 @@ -void set_data(unsigned char* magic, int version, FILE* target); -int copy_file(char* source_file, char* target_file); +#ifndef msvpvf_common_h +#define msvpvf_common_h + +#include +#include + +#ifndef ARRAYSIZE +#define ARRAYSIZE(x) \ + (sizeof(x)/sizeof((x)[0])) +#endif + +enum types { + TYPES_UNKNOWN = 0, + TYPES_VF, + TYPES_VEG +}; + +int set_file_information(FILE* target, uint8_t version, enum types type); +int get_file_information(FILE* input, uint8_t* version, enum types* type); + +#endif /* msvpvf_common_h */ diff -r fae1d67d8cfd -r 8f90d5addda9 src/common.c --- a/src/common.c Sun Oct 01 03:03:29 2023 -0400 +++ b/src/common.c Wed Mar 20 17:06:26 2024 -0400 @@ -1,39 +1,51 @@ -#include -#include +#include "common.h" + +#include + +/* hardcoded magic values; stored at 0x18... */ +static const uint8_t magic_veg[16] = {0xEF, 0x29, 0xC4, 0x46, 0x4A, 0x90, 0xD2, 0x11, 0x87, 0x22, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A}; +static const uint8_t magic_vf[16] = {0xF6, 0x1B, 0x3C, 0x53, 0x35, 0xD6, 0xF3, 0x43, 0x8A, 0x90, 0x64, 0xB8, 0x87, 0x23, 0x1F, 0x7F}; + +int set_file_information(FILE* target, uint8_t version, enum types type) { + const uint8_t* magic = (type == TYPES_VF) ? magic_vf : magic_veg; -void set_data(unsigned char* magic, uint16_t version, FILE* target) { - int i; - fseek(target, 0x46, SEEK_SET); - fputc(version, target); - for (i=0; i<=sizeof(*magic); ++i) { - fseek(target, 0x18+i, SEEK_SET); - fputc(magic[i], target); - } + if (fseek(target, 0x46, SEEK_SET)) + return -1; + + if (fputc(version, target) == EOF) + return -1; + + if (fseek(target, 0x18, SEEK_SET)) + return -1; + + if (fwrite(magic, sizeof(*magic), 16, target) < 16) + return -1; + + return 0; } -int copy_file(char* source_file, char* target_file) { - char ch[4096]; - FILE *source, *target; +int get_file_information(FILE* input, uint8_t* version, enum types* type) { + uint8_t magic[16] = {0}; - source = fopen(source_file, "rb"); + if (fseek(input, 0x46, SEEK_SET)) + return -1; + + *version = fgetc(input); + + if (fseek(input, 0x18, SEEK_SET)) + return -1; - if (source == NULL) - return 1; + /* read the WHOLE magic, then memcmp */ + if (fread(magic, sizeof(*magic), ARRAYSIZE(magic), input) < ARRAYSIZE(magic)) + return -1; - target = fopen(target_file, "wb"); - - if (target == NULL) { - fclose(source); - return 1; + if (!memcmp(magic, magic_veg, ARRAYSIZE(magic))) { + *type = TYPES_VEG; + } else if (!memcmp(magic, magic_vf, ARRAYSIZE(magic))) { + *type = TYPES_VF; + } else { + *type = TYPES_UNKNOWN; } - while (!feof(source)) { - size_t b = fread(ch, 1, sizeof(ch), source); - if (b) - fwrite(ch, 1, b, target); - } - - fclose(target); - fclose(source); return 0; } diff -r fae1d67d8cfd -r 8f90d5addda9 src/gui.c --- a/src/gui.c Sun Oct 01 03:03:29 2023 -0400 +++ b/src/gui.c Wed Mar 20 17:06:26 2024 -0400 @@ -1,8 +1,6 @@ /** * msvpvf GUI for Windows - * Copyright (c) Paper 2022 - * - * View this file with 4-tab spacing; if you don't it will be a formatting nightmare. + * 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 @@ -22,203 +20,375 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. **/ -#include -#include -#include + +/* 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 +#include #include +#include + #include -#include -#include -#include "../include/common.h" -#define _WIN32_WINNT 0x0400 -#define ARRAYSIZE(a) \ - sizeof(a)/sizeof(a[0]) +#include + +#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 -#ifdef _MSC_VER -#define strdup(p) _strdup(p) + +/* 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 -HWND hWndListBox, hWndComboBox, hWndVersion; -int16_t version = 13; -enum types { - vf, - veg -} type; -char file_name[257] = {'\0'}; +/* 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; -void display_file(char* path) { - /* Read the file to memory */ - FILE* file; - file = fopen(path, "rb"); - fseek(file, 0x46, SEEK_SET); - int f_version = fgetc(file)+1; - fseek(file, 0x18, SEEK_SET); - TCHAR p[32]; - switch (fgetc(file)) { - case 0xEF: - snprintf(p, 32, "File version: %s %d", "VEGAS Pro", f_version); - break; - case 0xF6: - snprintf(p, 32, "File version: %s %d", "Movie Studio", f_version); - break; - default: - snprintf(p, 32, "File version: %s %d", "Unknown", f_version); - break; +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"); } - SendMessage(hWndVersion, WM_SETTEXT, (WPARAM)0, (LPARAM)p); - fclose(file); +} + +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"); + } } -char* open_file(HWND hWnd) { - OPENFILENAME ofn; - char* filename = calloc(256, sizeof(char)); +/* 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; - ZeroMemory(&ofn, OPENFILENAME_SIZE_VERSION_400); - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = hWnd; - ofn.lpstrFile = filename; - ofn.lpstrFile[0] = '\0'; - ofn.nMaxFile = 256; - ofn.lpstrFilter = "Project files\0*.veg;*.vf\0All files\0*.*\0"; - ofn.nFilterIndex = 1; + get_file_information(file, &file_version, &file_type); - if (GetOpenFileName(&ofn) == 0) { - return " "; + 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; } - display_file(filename); + free(text); + fclose(file); - return filename; + return 0; } -void save_file(HWND hWnd, char* input_file) { - if (strcmp(input_file, " ") == 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; - } - OPENFILENAME ofn; - char output_file[256] = {'\0'}, *input_basename = strdup(input_file); - PathStripPath(input_basename); - int amt_of_ch = snprintf(output_file, 256, "PRO_V%d_", version); - if (256-amt_of_ch > 0) - strncat(output_file, input_basename, 256-amt_of_ch); - - ZeroMemory(&ofn, OPENFILENAME_SIZE_VERSION_400); - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = hWnd; - ofn.lpstrFile = output_file; - ofn.nMaxFile = 256; - ofn.lpstrFilter = "Movie Studio project files\0*.vf\0VEGAS Pro project files\0*.veg\0All files\0*.*\0"; - ofn.nFilterIndex = (int)type+1; - - if (GetSaveFileName(&ofn) == 0) { - return; + return -1; } - if (CopyFile((TCHAR*)input_file, (TCHAR*)output_file, 0) == 0) { - MessageBox(hWnd, TEXT("Failed to copy original project file! Does the destination file already exist?"), TEXT("Saving project failed!"), MB_ICONEXCLAMATION); - return; - } - FILE* output = fopen(output_file, "r+b"); - if (output == NULL) { - MessageBox(hWnd, TEXT("Failed to save project file!"), TEXT("Saving project failed!"), MB_ICONEXCLAMATION); - return; + 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); } - unsigned char magic_veg[] = {0xEF, 0x29, 0xC4, 0x46, 0x4A, 0x90, 0xD2, 0x11, 0x87, 0x22, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A}; - unsigned char magic_vf[] = {0xF6, 0x1B, 0x3C, 0x53, 0x35, 0xD6, 0xF3, 0x43, 0x8A, 0x90, 0x64, 0xB8, 0x87, 0x23, 0x1F, 0x7F}; + { + /* 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); - set_data(type == veg ? magic_veg : magic_vf, version-1, output); + 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 + } - fclose(output); + 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 */ - hWndComboBox = CreateWindow("ComboBox", NULL, + HWND combobox = CreateWindow(TEXT("ComboBox"), NULL, CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD | WS_VISIBLE | WS_OVERLAPPED | WS_VSCROLL, - (int)((225 - 50)/2), 30, 50, 200, + WINDOW_WIDTH * 7 / 18, WINDOW_HEIGHT * 3 / 20, WINDOW_WIDTH * 2 / 9, WINDOW_HEIGHT, hWnd, (HMENU)COMBOBOX, NULL, NULL); - TCHAR versions[][10] = {TEXT("8"), TEXT("9"), TEXT("10"), - TEXT("11"), TEXT("12"), TEXT("13"), - TEXT("14"), TEXT("15"), TEXT("16"), - TEXT("17"), TEXT("18"), TEXT("19")}; + 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 */ - int i = 0; - for (i = 0; i < ARRAYSIZE(versions); i++) { - SendMessage(hWndComboBox, (UINT)CB_ADDSTRING, (WPARAM)0, (LPARAM)versions[i]); - } - SendMessage(hWndComboBox, CB_SETCURSEL, (WPARAM)3, (LPARAM)0); - /* Open File */ - HWND open_button = CreateWindow("Button", "Open", WS_VISIBLE | WS_CHILD, (int)((225 - 50)/2), 5, 50, 20, hWnd, (HMENU)OPEN_FILE_BUTTON, NULL, NULL); + 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 */ - TCHAR listbox_items[][13] = {TEXT("VEGAS Pro"), TEXT("Movie Studio")}; - hWndListBox = CreateWindow("Listbox", NULL, WS_VISIBLE | WS_CHILD | LBS_STANDARD | LBS_NOTIFY, (int)((225 - 100)/2), 55, 100, 40, hWnd, (HMENU)LISTBOX, NULL, NULL); - for (i = 0; i < ARRAYSIZE(listbox_items); i++) { - int pos = (int)SendMessage(hWndListBox, LB_ADDSTRING, i, (LPARAM) listbox_items[i]); - SendMessage(hWndListBox, LB_SETITEMDATA, pos, (LPARAM) i); + 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); } - SendMessage(hWndListBox, LB_SETCURSEL, (WPARAM)0, (LPARAM)0); + /* Save File */ - HWND save_button = CreateWindow("Button", "Save", WS_VISIBLE | WS_CHILD, (int)((225 - 50)/2), 90, 50, 20, hWnd, (HMENU)SAVE_FILE_BUTTON, NULL, NULL); + 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 */ - hWndVersion = CreateWindow("Edit", "", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_READONLY | ES_CENTER | ES_MULTILINE | SS_CENTER, (int)((225 - 150)/2), 120, 150, 40, hWnd, (HMENU)VERSION, NULL, NULL); - if (open_button == NULL || save_button == NULL || hWndListBox == NULL || hWndComboBox == NULL) + 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); } -bool CALLBACK SetFont(HWND child, LPARAM font) { - SendMessage(child, WM_SETFONT, font, true); - return true; +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 ) { +LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_COMMAND: - if(HIWORD(wParam) == CBN_SELCHANGE) { - if (LOWORD(wParam) == COMBOBOX) - version = (int16_t)(8+SendMessage((HWND) lParam, (UINT) CB_GETCURSEL, (WPARAM) 0, (LPARAM) 0)); - if (LOWORD(wParam) == LISTBOX) - type = SendMessage((HWND) lParam, (UINT) LB_GETCURSEL, (WPARAM) 0, (LPARAM) 0); + 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) { + + switch (wParam) { case OPEN_FILE_BUTTON: - strncpy(file_name, open_file(hWnd), 256); - case COMBOBOX: - case LISTBOX: - case VERSION: + /* free(NULL) == no-op */ + free(file_path); + open_file(hWnd, &file_path); break; case SAVE_FILE_BUTTON: - save_file(hWnd, file_name); + save_file(hWnd, file_path, version, type); + break; + default: + break; } break; - case WM_CREATE: { + 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 */ - HDROP hDrop = (HDROP)wParam; - DragQueryFile(hDrop, 0, file_name, 256); - display_file(file_name); + 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 false; + return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR args, int ncmdshow) { @@ -227,12 +397,12 @@ wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hInstance = hInstance; - wc.lpszClassName = "msvpvf"; + 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, 100, 100, 225, 200, NULL, NULL, hInstance, NULL); + 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}; diff -r fae1d67d8cfd -r 8f90d5addda9 src/main.c --- a/src/main.c Sun Oct 01 03:03:29 2023 -0400 +++ b/src/main.c Wed Mar 20 17:06:26 2024 -0400 @@ -1,160 +1,186 @@ #include +#include #include #include -#include +//#include #include #include -#include "../include/common.h" -#ifdef _MSC_VER -#define strdup(p) _strdup(p) + +#include "common.h" + +/* non-portable functions */ +static inline char* msvpvf_internal_strdup(const char* str) { + size_t len = strlen(str) + 1; + + char* copy = malloc(len); + if (!copy) + return NULL; + + memcpy(copy, str, len); + return copy; +} + +/* source needs read permissions, target needs write permissions, both must be in binary mode */ +static inline int copy_file(FILE* source, FILE* target) { + char ch[4096]; + + while (!feof(source)) { + size_t b = fread(ch, 1, sizeof(ch), source); + if (b) + fwrite(ch, 1, b, target); + } + + return 0; +} + +static inline const char* type_to_string(enum types type) { + switch (type) { + case TYPES_VF: return "Movie Studio"; + case TYPES_VEG: return "Vegas Pro"; + case TYPES_UNKNOWN: + default: return "Unknown"; + } +} + +static inline const char* type_to_extension(enum types type) { + switch (type) { + case TYPES_VF: return "vf"; + case TYPES_VEG: + case TYPES_UNKNOWN: + default: return "veg"; + } +} + +#ifndef ARRAYSIZE +#define ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0])) #endif +static const char* help_text = + "msvpvf by Paper\n" + "usage: %s ... (arguments)\n" + "\n" + "arguments:\n" + " -t, --type one of [vf,veg] (required)\n" + " -v, --version version to convert to (required)\n" + " -h, --help show this screen (optional)\n"; + static struct option options_long[] = { - {"input", required_argument, NULL, 'i'}, - {"output", required_argument, NULL, 'o'}, {"version", required_argument, NULL, 'v'}, {"type", required_argument, NULL, 't'}, {"help", 0, NULL, 'h'} }; -char* strremove(char* str, const char* sub) { - size_t len = strlen(sub); - if (len > 0) { - char* p = str; - while ((p = strstr(p, sub)) != NULL) { - memmove(p, p + len, strlen(p + len) + 1); - } - } - return str; -} +int main(int argc, char *argv[]) { + uint8_t version = 0; + enum types type = TYPES_UNKNOWN; -int main(int argc, char *argv[]) { - int c, option_index = 0; - unsigned char magic[16]; - FILE* outfile; - struct arguments { - char input[256]; - char output[256]; - int version; - char type[4]; - } args; - strcpy(args.input, " "); - strcpy(args.output, " "); - args.version = -1; - strcpy(args.type, " "); - - while ((c = getopt_long(argc, argv, "i:o:v:t:h", options_long, &option_index)) != -1) - switch(c) { - case 'i': - strncpy(args.input, optarg, sizeof(args.input)-1); /* subtract 1 to make sure it's "null-safe" */ - break; - case 'o': - strncpy(args.output, optarg, sizeof(args.output)-1); - break; + int c; + int option_index = 0; + while ((c = getopt_long(argc, argv, "v:t:h", options_long, &option_index)) != -1) { + /* option argument */ + switch (c) { case 'v': - args.version = abs(atoi(strdup(optarg))); /* abs() for possible negative inputs */ + version = atoi(optarg); break; case 't': - strncpy(args.type, optarg, sizeof(args.type)-1); + if (!strcmp(optarg, "vf")) { + type = TYPES_VF; + } else if (!strcmp(optarg, "veg")) { + type = TYPES_VEG; + } else { + fprintf(stderr, "[ERROR]: Received an invalid type parameter!\n"); + printf(help_text, argv[0]); + } + break; case 'h': default: - printf("msvpvf by Paper\nusage: %s (-i/--input) infile [(-o/--output) outfile] (-v/--version) version (-t/--type) [vf, veg]\n", argv[0]); - return 0; + printf(help_text, argv[0]); + break; } - if (argc == 1) { - printf("msvpvf by Paper\nusage: %s (-i/--input) infile [(-o/--output) outfile] (-v/--version) version (-t/--type) [vf, veg]\n", argv[0]); - return 0; } - if (strcmp(args.input, " ") == 0) { - printf("Input file name?\n"); - fflush(stdout); - fgets(args.input, sizeof(args.input)-1, stdin); - args.input[strcspn(args.input, "\r\n")] = 0; - } - if (access(args.input, F_OK) != 0) { - fprintf(stderr, "Input file \"%s\" doesn't exist! Exiting.", args.input); + + if (argc <= optind) { + fprintf(stderr, "[ERROR]: Missing input file!\n"); + printf(help_text, argv[0]); return 1; } - FILE* input_file = fopen(args.input, "r"); - if (fgetc(input_file) == EOF) { - fprintf(stderr, "Input file \"%s\" is empty.", args.input); - fclose(input_file); + + if (!version || !type) { + printf(help_text, (argc > 0) ? argv[0] : "msvpvf"); return 1; } - fseek(input_file, 0x46, SEEK_SET); - printf("Input file version: %d\n", fgetc(input_file)); - fseek(input_file, 0x18, SEEK_SET); - int file_version = fgetc(input_file); - printf("Input file type: "); - switch (file_version) { - case 0xEF: - printf("VEGAS Pro\n\n"); - break; - case 0xF6: - printf("Movie Studio\n\n"); - break; - default: - printf("Unknown\n\n"); - break; - } - int* ptr = &args.version; - if (args.version == -1) { - printf("What version of VEGAS would you like to spoof to?: "); - fflush(stdout); - scanf("%d", ptr); - } - if (strcmp(args.type, " ") == 0) { - printf("Would you like it to be VEGAS Pro or Movie Studio? [veg/vf]: "); - fflush(stdout); - scanf("%3s", args.type); + + /* this progressively */ + while (optind < argc) { + const char* input = argv[optind++]; + FILE* output_file = NULL; + + { + uint8_t file_version = 0; + enum types file_type = TYPES_UNKNOWN; + + printf("Input file name: %s\n", input); + /* print information about the input file */ + FILE* input_file = fopen(input, "rb"); + if (!input_file) { + fprintf(stderr, "[ERROR]: Error opening input file %s!\n\n", input); + continue; + } + + if (fgetc(input_file) == EOF) { + fprintf(stderr, "[ERROR]: Input file \"%s\" is empty.\n", input); + fclose(input_file); + continue; + } + + if (get_file_information(input_file, &file_version, &file_type)) { + fprintf(stderr, "[ERROR]: Failed to get file information for input file \"%s\"!\n", input); + fclose(input_file); + continue; + } + + printf("Input file version: %u\n", file_version); + printf("Input file type: %s\n\n", type_to_string(file_type)); + + { + /* open the output file... */ + char* basec = msvpvf_internal_strdup(input); + char* bname = basename(basec); + int ext = strrchr(bname, '.') - bname; + + /* create the output filename */ + int needed_size = snprintf(NULL, 0, "V%u_%.*s.%s", version, ext, bname, type_to_extension(type)); + char* output = malloc((needed_size + 1) * sizeof(char)); + if (!output) { + fprintf(stderr, "[ERROR]: Failed to allocate memory for output string!\n"); + free(basec); + return 1; + } + + snprintf(output, needed_size + 1, "V%u_%.*s.%s", version, ext, bname, type_to_extension(type)); + printf("Output filename: %s\n", output); + + output_file = fopen(output, "w+b"); + if (!output_file) { + fprintf(stderr, "[ERROR]: Failed to open output file %s! Do you have write permissions?\n", output); + free(basec); + free(output); + continue; + } + free(basec); + free(output); + } + + copy_file(input_file, output_file); + + fflush(stdout); + fclose(input_file); + } + + set_file_information(output_file, version, type); + fclose(output_file); } - fflush(stdout); - if (strcmp(args.output, " ") == 0) { /* string manipulation hell */ - char* temp = (char*)calloc(256, sizeof(char)); - temp[0] = '\0'; - char str_version[4]; - sprintf(str_version, "V%d", args.version); - strncat(temp, str_version, 4); - strncat(temp, "_", 2); - strncat(temp, basename(args.input), 248); - strcpy(temp, strremove(temp, strrchr(basename(args.input), ('.')))); /* remove file extension */ - strncat(temp, ".", 2); - strncat(temp, args.type, 4); - strncpy(args.output, temp, 255); - free(temp); - } - if (strcmp(args.type, "veg") == 0) { - const unsigned char T[] = {0xEF, 0x29, 0xC4, 0x46, 0x4A, 0x90, 0xD2, 0x11, 0x87, 0x22, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A}; - for (option_index = 0; option_index <= 15; option_index++) { /* this line is repeated, but it's probably best to just leave it be. */ - magic[option_index] = T[option_index]; - } - } else if (strcmp(args.type, "vf") == 0) { - const unsigned char T[] = {0xF6, 0x1B, 0x3C, 0x53, 0x35, 0xD6, 0xF3, 0x43, 0x8A, 0x90, 0x64, 0xB8, 0x87, 0x23, 0x1F, 0x7F}; - for (option_index = 0; option_index <= 15; option_index++) { - magic[option_index] = T[option_index]; - } - } else { - fprintf(stderr, "Type %s is invalid!", args.type); - return 1; - } - copy_file(args.input, args.output); -#ifdef _WIN32 /* disallowed characters in filenames */ - if (strcspn(args.input, "<>:\"/\\|?*") == strlen(args.input)+1) { -#elif defined(__unix__) - if (strcspn(args.input, "/") == strlen(args.input)+1) { -#else - if (NULL) { -#endif - fprintf(stderr, "Invalid output filename detected! Exiting..."); - return 1; - } - outfile = fopen(args.output, "r+b"); - if (outfile == NULL) { - fprintf(stderr, "Failed to open file %s! Do you have write permissions?", args.output); - return 1; - } - set_data(&magic, args.version, outfile); - fclose(outfile); + return 0; }