diff --git a/CommonCfg/CommonCfg.vcproj b/CommonCfg/CommonCfg.vcproj
new file mode 100644
index 0000000..14fdbc4
--- /dev/null
+++ b/CommonCfg/CommonCfg.vcproj
@@ -0,0 +1,160 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CommonCfg/Config.c b/CommonCfg/Config.c
new file mode 100644
index 0000000..60ff9c2
--- /dev/null
+++ b/CommonCfg/Config.c
@@ -0,0 +1,141 @@
+#include "stdafx.h"
+
+const Config DEFAULT_CONFIG = {
+ {0}, // Use default (My Documents/Screenshots)
+ FALSE, // Don't upload screenshots
+ TRUE, // Do keep screenshots
+ FALSE, // Don't start helper on startup
+ HSC_WIN10 // Emulate Win7+ "Snipping Tool" shortcut
+};
+
+#define OpenRootKey(hkey,access) \
+ RegCreateKeyEx(HKEY_CURRENT_USER, \
+ _T(ROOT_KEYNAME), \
+ 0, \
+ NULL, \
+ REG_OPTION_NON_VOLATILE, \
+ access, \
+ NULL, \
+ hkey, \
+ NULL)
+
+// Enumeration callback for GetConfig
+static BOOL
+ConfigCallback(HKEY hKey,
+ LPTSTR szValName,
+ DWORD dwValSize,
+ DWORD dwValType,
+ PVOID pData)
+{
+ Config *cfg = pData;
+ if (!cfg) return FALSE;
+
+ CopyIfMatch(hKey, cfg,
+ ScreenshotDir, REG_PATH_SZ)
+ else CopyIfMatch(hKey, cfg,
+ Upload, REG_DWORD)
+ else CopyIfMatch(hKey, cfg,
+ KeepScreenshots, REG_DWORD)
+ else CopyIfMatch(hKey, cfg,
+ HelperStartup, REG_DWORD)
+ else CopyIfMatch(hKey, cfg,
+ HelperShortcut, REG_DWORD)
+
+ return TRUE;
+}
+
+Config *
+GetConfig(void)
+{
+ HKEY hkRootKey = NULL;
+ BOOL bRes;
+ DWORD dwErr;
+ HANDLE hHeap = GetProcessHeap();
+ Config *cfg = HeapAlloc(hHeap, 0, sizeof(Config));
+
+ if (!cfg) return NULL;
+
+ // Copy in the default config
+ CopyMemory(cfg, &DEFAULT_CONFIG, sizeof(Config));
+
+ if ((dwErr = OpenRootKey(&hkRootKey, KEY_READ))) {
+ HeapFree(hHeap, 0, cfg);
+ SetLastError(dwErr);
+ return NULL;
+ }
+
+ bRes = RegGetAllValues(hkRootKey, ConfigCallback, cfg);
+ dwErr = GetLastError();
+ RegCloseKey(hkRootKey);
+
+ if (!bRes) {
+ FreeConfig(cfg);
+ SetLastError(dwErr);
+ return NULL;
+ }
+
+ // If ScreenshotDir is NULL, set it to My Documents
+ if (!strlen(cfg->ScreenshotDir)) {
+ HRESULT hrRes;
+
+ // afaict, My Pictures isn't an easily accessible folder.
+ // There's CSIDL_MYPICTURES, but that's XP-only (and also
+ // points to a virtual folder???).
+ hrRes = SHGetFolderPath(NULL,
+ CSIDL_PERSONAL,
+ NULL,
+ SHGFP_TYPE_CURRENT,
+ cfg->ScreenshotDir);
+
+ if (FAILED(hrRes)) {
+ // TODO: error management
+ // For now, just empty the string
+ *cfg->ScreenshotDir = 0;
+ } else {
+ PathAppend(cfg->ScreenshotDir,
+ _T("Screenshots"));
+ }
+ }
+
+ // TODO: Detect startup
+
+ return cfg;
+}
+
+BOOL
+SaveConfig(Config *cfg)
+{
+ HKEY hkRootKey;
+ DWORD dwErr;
+
+ if (!cfg) return FALSE;
+
+ if ((dwErr = OpenRootKey(&hkRootKey, KEY_WRITE))) {
+ SetLastError(dwErr);
+ return FALSE;
+ }
+
+ StoreConfig(hkRootKey, cfg,
+ ScreenshotDir, REG_SZ)
+ StoreConfig(hkRootKey, cfg,
+ Upload, REG_DWORD)
+ StoreConfig(hkRootKey, cfg,
+ KeepScreenshots, REG_DWORD)
+ StoreConfig(hkRootKey, cfg,
+ HelperStartup, REG_DWORD)
+ StoreConfig(hkRootKey, cfg,
+ HelperShortcut, REG_DWORD)
+
+ RegCloseKey(hkRootKey);
+ return TRUE;
+}
+
+void
+FreeConfig(Config *cfg)
+{
+ HANDLE hHeap = GetProcessHeap();
+ if (!cfg) return;
+
+ HeapFree(hHeap, 0, cfg);
+ return;
+}
\ No newline at end of file
diff --git a/CommonCfg/Export/Config.h b/CommonCfg/Export/Config.h
new file mode 100644
index 0000000..e036eb8
--- /dev/null
+++ b/CommonCfg/Export/Config.h
@@ -0,0 +1,86 @@
+#pragma once
+
+typedef struct _MultiString {
+ LPTSTR * Values; // Array of string values
+ size_t Count; // Length of Values
+ size_t TotalLength; // Used by registry helpers.
+ // NOTE: This length includes the NULL
+ // terminator characters of each string
+ DWORD Reserved[2]; // Used internally
+} MultiString;
+
+// Initializes an existing MultiString array, with the given length.
+// If dwInitLen is 0, no entries are pre-allocated.
+BOOL
+MultiString_Init(MultiString *ms,
+ DWORD dwInitLen);
+
+// Appends a copy of szNew to the end of the list.
+//
+// NOTE: A new string with the same length as szNew is allocated and
+// stored in ms. This makes future internal freeing easier, but means
+// the function caller is responsible for freeing the parameter.
+BOOL
+MultiString_Append(MultiString *ms,
+ LPCTSTR szNew);
+
+// Removes the string at dwIdx, freeing the memory used.
+BOOL
+MultiString_Remove(MultiString *ms,
+ DWORD dwIdx);
+
+// Replaces the string at dwIdx with szNew. Allocations are performed
+// similar to MultiString_Append.
+BOOL
+MultiString_Replace(MultiString *ms,
+ DWORD dwIdx,
+ LPCTSTR szNew);
+
+// Frees all memory in a MultiString structure.
+void
+MultiString_Dispose(MultiString *ms);
+
+typedef enum _HelperShortcut {
+ HSC_MACOS, // Pretend to be like MacOS (Win+Shift+4)
+ HSC_WIN10, // Pretend to be like Windows (Win+Shift+S)
+ HSC_CUSTOM // Use a custom keyboard shortcut
+} HlprKbd;
+
+typedef struct _GlobalConfig {
+ TCHAR ScreenshotDir[MAX_PATH];
+ BOOL Upload;
+ BOOL KeepScreenshots;
+ BOOL HelperStartup;
+ HlprKbd HelperShortcut;
+} Config;
+
+typedef enum _AuthMethod {
+ CFG_NOAUTH, // Anonymous HTTP requests
+ CFG_BASICAUTH, // Use HTTP Basic auth
+ CFG_DIGESTAUTH, // Use HTTP Digest auth
+ CFG_TOKENAUTH // Use a custom Authorization header
+} AuthMethod;
+
+typedef struct _UploadConfig {
+ LPTSTR Name;
+ LPTSTR URL;
+ LPTSTR Method;
+ /*
+ * NOTE: If AuthType == CFG_TOKENAUTH, Username is blank/NULL and
+ * Password is the token.
+ */
+ LPTSTR Username;
+ LPTSTR Password;
+ AuthMethod HTTPAuthMethod;
+
+ MultiString Headers;
+ MultiString Parameters;
+} UploadConfig;
+
+
+// Gets the current configuration from the registry.
+Config *GetConfig(void);
+// Saves the Config object to the registry.
+BOOL SaveConfig(Config *cfg);
+// Frees all memory associated with the Config object.
+void FreeConfig(Config *cfg);
\ No newline at end of file
diff --git a/CommonCfg/MultiString.c b/CommonCfg/MultiString.c
new file mode 100644
index 0000000..431b6a4
--- /dev/null
+++ b/CommonCfg/MultiString.c
@@ -0,0 +1,196 @@
+#include "stdafx.h"
+
+#define STRSAFE_NO_DEPRECATE
+#include
+
+#define CurAlloc Reserved[0]
+
+
+BOOL
+MultiString_Init(MultiString *ms,
+ DWORD dwInitLen)
+{
+ HANDLE hHeap = GetProcessHeap();
+
+ if (!ms) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ if (dwInitLen) {
+ ms->CurAlloc = dwInitLen;
+ ms->Values = HeapAlloc(hHeap,
+ HEAP_ZERO_MEMORY,
+ sizeof(LPTSTR) * dwInitLen);
+ if (!ms->Values) return FALSE;
+ ms->Count = 0;
+ } else {
+ // Otherwise, this amounts to a zero-out
+ ZeroMemory(ms, sizeof(MultiString));
+ }
+
+ return TRUE;
+}
+
+__inline static LPTSTR
+CreateStrCopy(LPCTSTR szStr)
+{
+ HRESULT hrRes;
+ LPTSTR szNewCopy;
+ // dwLen needs to include the NULL terminator
+ size_t dwLen = _tcslen(szStr) + 1;
+ HANDLE hHeap = GetProcessHeap();
+
+ // Allocate the new string
+ szNewCopy = HeapAlloc(hHeap,
+ HEAP_ZERO_MEMORY,
+ sizeof(TCHAR) * dwLen);
+ if (!szNewCopy) return NULL;
+
+ // Copy the actual data over
+ hrRes = StringCchCopy(szNewCopy,
+ dwLen,
+ szStr);
+ if (FAILED(hrRes)) {
+ HeapFree(hHeap, 0, szNewCopy);
+ return NULL;
+ }
+
+ return szNewCopy;
+}
+
+BOOL
+MultiString_Append(MultiString *ms,
+ LPCTSTR szNew)
+{
+ LPTSTR szNewCopy = NULL;
+ DWORD dwNewLen;
+ HANDLE hHeap = GetProcessHeap();
+
+ if (!ms || !szNew) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ dwNewLen = (DWORD)_tcslen(szNew) + 1;
+
+ // Do we need to allocate more room for pointers?
+ if (ms->Values == NULL) {
+ // Oops, we have nothing! Let's allocate a couple
+ if (!MultiString_Init(ms, 3)) {
+ return FALSE;
+ }
+ } else if (ms->CurAlloc == ms->Count) {
+ DWORD dwNewAlloc = ms->CurAlloc + 3;
+ LPTSTR *pszNew = HeapReAlloc(hHeap,
+ 0,
+ ms->Values,
+ sizeof(LPTSTR) * dwNewAlloc);
+ if (!pszNew) {
+ // ReAlloc failed :<
+ return FALSE;
+ }
+ ms->CurAlloc = dwNewAlloc;
+ ms->Values = pszNew;
+ }
+
+ szNewCopy = CreateStrCopy(szNew);
+ if (!szNewCopy) return FALSE;
+
+ // All set, append to the end and increment Count
+ ms->Values[ms->Count++] = szNewCopy;
+ ms->TotalLength += dwNewLen;
+
+ return TRUE;
+}
+
+BOOL
+MultiString_Remove(MultiString *ms,
+ DWORD dwIdx)
+{
+ DWORD dwLen = 0;
+ HANDLE hHeap = GetProcessHeap();
+
+ if (!ms) return FALSE;
+
+ if (dwIdx >= ms->Count) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ } else if (ms->Values[dwIdx] != NULL) {
+ dwLen = (DWORD)_tcslen(ms->Values[dwIdx]) + 1;
+
+ HeapFree(hHeap, 0, ms->Values[dwIdx]);
+ }
+
+
+ // Shift down the remaining strings
+ if (dwIdx < ms->Count - 1) {
+ size_t i;
+ for (i = dwIdx; i < ms->Count - 1; i++) {
+ ms->Values[i] = ms->Values[i + 1];
+ }
+ }
+
+ ms->Count--;
+ ms->TotalLength -= dwLen;
+
+ return TRUE;
+}
+
+BOOL
+MultiString_Replace(MultiString *ms,
+ DWORD dwIdx,
+ LPCTSTR szNew)
+{
+ DWORD dwOldLen = 0,
+ dwNewLen = 0;
+ LPTSTR szTmp;
+ HANDLE hHeap = GetProcessHeap();
+
+ if (!ms || !szNew) return FALSE;
+
+ if (dwIdx >= ms->Count) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ dwNewLen = (DWORD)_tcslen(szNew) + 1;
+
+ // Create the copied new string
+ szTmp = CreateStrCopy(szNew);
+ if (!szTmp) return FALSE;
+
+ // Perform the switcheroo
+ if (ms->Values[dwIdx] != NULL) {
+ dwOldLen = (DWORD)_tcslen(ms->Values[dwIdx]) + 1;
+
+ HeapFree(hHeap, 0, ms->Values[dwIdx]);
+ }
+
+ ms->Values[dwIdx] = szTmp;
+ ms->TotalLength = ms->TotalLength + dwNewLen - dwOldLen;
+
+ return TRUE;
+}
+
+void
+MultiString_Dispose(MultiString *ms)
+{
+ HANDLE hHeap = GetProcessHeap();
+ DWORD i;
+
+ if (!ms) return;
+
+ if (ms->Values != NULL) {
+ for (i = 0; i < ms->Count; i++) {
+ HeapFree(hHeap, 0, ms->Values[i]);
+ }
+
+ HeapFree(hHeap, 0, ms->Values);
+ ms->Values = NULL;
+ }
+ ms->Count = 0;
+ ms->CurAlloc = 0;
+
+ return;
+}
\ No newline at end of file
diff --git a/CommonCfg/ReadMe.txt b/CommonCfg/ReadMe.txt
new file mode 100644
index 0000000..4cc1b4c
--- /dev/null
+++ b/CommonCfg/ReadMe.txt
@@ -0,0 +1,29 @@
+========================================================================
+ STATIC LIBRARY : CommonCfg Project Overview
+========================================================================
+
+AppWizard has created this CommonCfg library project for you.
+This file contains a summary of what you will find in each of the files that
+make up your CommonCfg application.
+
+
+CommonCfg.vcproj
+ This is the main project file for VC++ projects generated using an Application Wizard.
+ It contains information about the version of Visual C++ that generated the file, and
+ information about the platforms, configurations, and project features selected with the
+ Application Wizard.
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+StdAfx.h, StdAfx.cpp
+ These files are used to build a precompiled header (PCH) file
+ named CommonCfg.pch and a precompiled types file named StdAfx.obj.
+
+/////////////////////////////////////////////////////////////////////////////
+Other notes:
+
+AppWizard uses "TODO:" comments to indicate parts of the source code you
+should add to or customize.
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/CommonCfg/RegHelper.c b/CommonCfg/RegHelper.c
new file mode 100644
index 0000000..7047020
--- /dev/null
+++ b/CommonCfg/RegHelper.c
@@ -0,0 +1,280 @@
+#include "stdafx.h"
+
+#define STRSAFE_NO_DEPRECATE
+#include
+
+// Used by CopyRegValue specifically for copying
+// REG_MULTI_SZ values, because those in particular are
+// really funky.
+static BOOL
+CopyMultiValue(HKEY hKey,
+ PVOID pVal,
+ LPCTSTR szName,
+ DWORD dwSize)
+{
+ int iStrCount = 0;
+ LPTSTR szMulti = NULL,
+ ptr;
+ BOOL bWasNull = FALSE;
+ LONG lRes;
+ MultiString *msData = (MultiString *)pVal;
+
+ HANDLE hHeap = GetProcessHeap();
+
+ szMulti = HeapAlloc(hHeap,
+ HEAP_ZERO_MEMORY,
+ dwSize + sizeof(TCHAR));
+ if (!szMulti) return FALSE;
+
+ // Get the multi-string value
+ if (!(lRes = RegQueryValueEx(hKey,
+ szName,
+ 0,
+ NULL,
+ szMulti,
+ &dwSize)))
+ {
+ HeapFree(hHeap, 0, szMulti);
+ SetLastError(lRes);
+ return FALSE;
+ }
+
+ for (ptr = szMulti; (ptr - szMulti) < (LONG)dwSize; ptr++) {
+ if (*ptr == 0) {
+ if (bWasNull) {
+ // Two NULLs in a row, we're done!
+ break;
+ } else {
+ // End of string, add to the list
+ bWasNull = TRUE;
+ iStrCount++;
+ }
+ }
+ bWasNull = FALSE;
+ }
+
+ if (!bWasNull) {
+ // Not properly NULL terminated, sigh...
+ // Make sure to count the last string.
+ iStrCount++;
+ }
+
+ if (!MultiString_Init(msData, iStrCount)) {
+ HeapFree(hHeap, 0, szMulti);
+ return FALSE;
+ } else {
+ int i;
+ for (i = 0, ptr = szMulti; i < iStrCount; i++, ptr += strlen(ptr) + 1) {
+ if (!MultiString_Append(msData, ptr)) {
+ DWORD err = GetLastError();
+
+ HeapFree(hHeap, 0, szMulti);
+ MultiString_Dispose(msData);
+
+ SetLastError(err);
+ return FALSE;
+ }
+ }
+ }
+
+ HeapFree(hHeap, 0, szMulti);
+
+ return TRUE;
+}
+
+BOOL
+CopyRegValue(HKEY hKey,
+ PVOID pVal,
+ LPCTSTR szName,
+ DWORD dwType,
+ DWORD dwSize)
+{
+ HANDLE hHeap = GetProcessHeap();
+ PVOID pData = pVal;
+ LONG lRes;
+
+ if (pVal == NULL ||
+ szName == NULL ||
+ hKey == NULL) {
+ return FALSE;
+ }
+
+ if (dwType == REG_BINARY ||
+ dwType == REG_SZ) {
+ pData = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, dwSize);
+ if (!pData) return FALSE;
+ *(PVOID *)pVal = pData;
+ } else if (dwType == REG_MULTI_SZ) {
+ // REG_MULTI_SZ is complex enough to warrant its own
+ // function
+ return CopyMultiValue(hKey, pVal, szName, dwSize);
+ }
+
+ if ((lRes = RegQueryValueEx(hKey,
+ szName,
+ NULL,
+ NULL,
+ pData,
+ &dwSize)) != ERROR_SUCCESS)
+ {
+ SetLastError(lRes);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// Enumerates across all values in a key, running the given
+// RegEnumCallback. Use `pData` to pass application-defined
+// data to your enumerator callback.
+BOOL RegGetAllValues(HKEY hKey,
+ RegEnumCallback cb,
+ PVOID pData)
+{
+ LONG lRes;
+ TCHAR szValName[512];
+ DWORD dwNameLen = 512,
+ dwIdx,
+ dwValType,
+ dwValSize;
+ if (!hKey || !cb) return FALSE;
+
+ for (dwIdx = 0;
+ dwNameLen = 512, // reset dwNameLen first
+ !(lRes = RegEnumValue(hKey,
+ dwIdx,
+ szValName,
+ &dwNameLen,
+ 0,
+ &dwValType,
+ NULL,
+ &dwValSize));
+ dwIdx++) {
+ // Enforce null termination
+ szValName[min(511, dwNameLen)] = 0;
+ // pass to callback
+ if (!cb(hKey,
+ szValName,
+ dwValSize,
+ dwValType,
+ pData)) {
+ // Callback returned failure, bail out
+ return FALSE;
+ }
+ }
+
+ if (lRes != ERROR_NO_MORE_ITEMS) {
+ // Some enumeration error happened, store it
+ // and report the failure
+ SetLastError(lRes);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL
+RegStoreMultiString(HKEY hKey,
+ LPTSTR szValName,
+ MultiString *msData)
+{
+ LPTSTR szMulti;
+ DWORD dwMultiSize;
+ LONG lRes;
+ HANDLE hHeap = GetProcessHeap();
+
+ if (!msData->TotalLength) return FALSE;
+
+ // Total size + second NULL terminator
+ dwMultiSize = (DWORD)msData->TotalLength + 1;
+ szMulti = HeapAlloc(hHeap,
+ HEAP_ZERO_MEMORY,
+ dwMultiSize * sizeof(TCHAR));
+ if (!szMulti) return FALSE;
+
+ // Copy all the strings into one buffer, as per REG_MULTI_SZ
+ {
+ DWORD i, dwCurLen;
+ for (i = 0, dwCurLen = 0;
+ i < msData->Count && dwCurLen < dwMultiSize;
+ dwCurLen += (DWORD)_tcslen(msData->Values[i++]) + 1) {
+ HRESULT hrRes = StringCchCopy(&szMulti[dwCurLen],
+ _tcslen(msData->Values[i]) + 1,
+ msData->Values[i]);
+ if (FAILED(hrRes)) {
+ HeapFree(hHeap, 0, szMulti);
+ return FALSE;
+ }
+ }
+ }
+
+ lRes = RegSetValueEx(hKey,
+ szValName,
+ 0,
+ REG_MULTI_SZ,
+ szMulti,
+ dwMultiSize);
+
+ HeapFree(hHeap, 0, szMulti);
+
+ if (lRes != ERROR_SUCCESS) {
+ SetLastError(lRes);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// Stores a value in the registry, making assumptions based on the
+// key type.
+BOOL
+RegStoreValue(HKEY hKey,
+ LPTSTR szValName,
+ PVOID pData,
+ DWORD dwValType)
+{
+ DWORD dwStoreSize;
+ LONG lRes;
+ if (!hKey || !szValName || !pData) return FALSE;
+
+ switch (dwValType) {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ dwStoreSize = (DWORD)_tcslen((LPTSTR)pData) + sizeof(TCHAR);
+ break;
+ case REG_DWORD:
+ dwStoreSize = sizeof(DWORD);
+ break;
+#ifdef QWORD
+ case REG_QWORD:
+ dwStoreSize = sizeof(QWORD);
+ break;
+#endif
+ case REG_BINARY:
+ // TODO: handle REG_BINARY? will we ever use it?
+ OutputDebugString(_T("REG_BINARY attempted to be stored! Unsupported"));
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ case REG_MULTI_SZ:
+ return RegStoreMultiString(hKey,
+ szValName,
+ (MultiString *)pData);
+ default:
+ // ignore
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ lRes = RegSetValueEx(hKey,
+ szValName,
+ 0,
+ dwValType,
+ pData,
+ dwStoreSize);
+ if (lRes != ERROR_SUCCESS) {
+ SetLastError(lRes);
+ return FALSE;
+ }
+
+ return TRUE;
+}
\ No newline at end of file
diff --git a/CommonCfg/RegHelper.h b/CommonCfg/RegHelper.h
new file mode 100644
index 0000000..415fad0
--- /dev/null
+++ b/CommonCfg/RegHelper.h
@@ -0,0 +1,68 @@
+// Helper functions for handling the registry
+#pragma once
+
+// Used behind CopyIfMatch. Copies a given registry value to
+// another location, regardless of type.
+BOOL CopyRegValue(HKEY hKey,
+ PVOID pVal,
+ LPCTSTR szName,
+ DWORD dwType,
+ DWORD dwSize);
+
+// Callback for RegGetAllValues
+typedef BOOL (*RegEnumCallback)(HKEY hKey,
+ LPTSTR szValName,
+ DWORD dwValSize,
+ DWORD dwValType,
+ PVOID pData);
+
+// Enumerates across all values in a key, running the given
+// RegEnumCallback. Use `pData` to pass application-defined
+// data to your enumerator callback.
+BOOL RegGetAllValues(HKEY hKey,
+ RegEnumCallback cb,
+ PVOID pData);
+
+// Wrapper function for storing reg values, regardless of type
+BOOL RegStoreValue(HKEY hKey,
+ LPTSTR szValName,
+ PVOID pData,
+ DWORD dwValType);
+
+// Used primarily by RegEnumCallback, hence the hardcoded
+// var names.
+// You might want to check out one of the Get*Config functions
+// for more background on how to use this, but it should make the
+// callback function as easy as:
+//
+// CopyIfMatch(hKey, myStruct,
+// myField, REG_SZ) // or any type
+// else CopyIfMatch(hKey, myStruct,
+// myOtherField, REG_DWORD) // also note: no semicolons
+// // ... and so on
+#define CopyIfMatch(hk,x,name,type) \
+ if (!_tcsncmp(_T(#name), \
+ szValName, \
+ _tcslen(_T(#name))) \
+ && (dwValType & 0xfff) == type) { \
+ if (!CopyRegValue(hk, \
+ &((x)->name), \
+ _T(#name), \
+ type, \
+ dwValSize)) { \
+ return FALSE; \
+ } \
+ }
+
+// Helper macro for Save*Config functions. Assumes a BOOL-returning
+// function in a vacuum.
+#define StoreConfig(hk,x,name,type) \
+ if (!RegStoreValue(hk, _T(#name), &(x)->name, type)) { \
+ DWORD dwRegErr = GetLastError(); \
+ RegCloseKey(hk); \
+ return FALSE; \
+ }
+
+#define ROOT_KEYNAME "SOFTWARE\\Grabby"
+// Hack to handle REG_SZ values that are actually TCHAR[MAX_PATH].
+#define REG_PATH_SZ (0xf000 & REG_SZ)
\ No newline at end of file
diff --git a/CommonCfg/UploadConfig.c b/CommonCfg/UploadConfig.c
new file mode 100644
index 0000000..a21da53
--- /dev/null
+++ b/CommonCfg/UploadConfig.c
@@ -0,0 +1,65 @@
+#include "stdafx.h"
+
+static BOOL
+UploadConfigCb(HKEY hKey,
+ LPTSTR szValName,
+ DWORD dwValSize,
+ DWORD dwValType,
+ PVOID pData)
+{
+ UploadConfig *ucfg = pData;
+ if (!ucfg) return FALSE;
+
+ CopyIfMatch(hKey, ucfg,
+ Name, REG_SZ)
+ else CopyIfMatch(hKey, ucfg,
+ URL, REG_SZ)
+ else CopyIfMatch(hKey, ucfg,
+ Method, REG_SZ)
+ else CopyIfMatch(hKey, ucfg,
+ Username, REG_SZ)
+ else CopyIfMatch(hKey, ucfg,
+ Password, REG_SZ)
+ else CopyIfMatch(hKey, ucfg,
+ HTTPAuthMethod, REG_DWORD)
+ else CopyIfMatch(hKey, ucfg,
+ Headers, REG_MULTI_SZ)
+ else CopyIfMatch(hKey, ucfg,
+ Parameters, REG_MULTI_SZ)
+
+ return TRUE;
+}
+
+UploadConfig *
+GetUploadConfig(void)
+{
+
+ HKEY hkUploadKey = NULL;
+ HANDLE hHeap = GetProcessHeap();
+ LONG lRes;
+ UploadConfig *ucfg = HeapAlloc(hHeap,
+ HEAP_ZERO_MEMORY,
+ sizeof(UploadConfig));
+
+ if (!ucfg) return NULL;
+
+ // TODO: defaults?
+ lRes = RegOpenKeyEx(HKEY_CURRENT_USER,
+ _T(ROOT_KEYNAME "\\Upload"),
+ 0,
+ KEY_READ,
+ &hkUploadKey);
+
+ if (lRes) {
+ SetLastError(lRes);
+ HeapFree(hHeap, 0, ucfg);
+ return NULL;
+ }
+
+ if (!RegGetAllValues(hkUploadKey, UploadConfigCb, ucfg)) {
+ HeapFree(hHeap, 0, ucfg);
+ return NULL;
+ }
+
+ return ucfg;
+}
\ No newline at end of file
diff --git a/CommonCfg/stdafx.c b/CommonCfg/stdafx.c
new file mode 100644
index 0000000..1577c4e
--- /dev/null
+++ b/CommonCfg/stdafx.c
@@ -0,0 +1 @@
+#include "stdafx.h"
\ No newline at end of file
diff --git a/CommonCfg/stdafx.h b/CommonCfg/stdafx.h
new file mode 100644
index 0000000..adcd07b
--- /dev/null
+++ b/CommonCfg/stdafx.h
@@ -0,0 +1,17 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+#include
+#include
+#include
+#include
+
+// TODO: reference additional headers your program requires here
+#include "RegHelper.h"
+#include "Export/Config.h"
\ No newline at end of file
diff --git a/Grabby.sln b/Grabby.sln
index 3e07894..b606201 100644
--- a/Grabby.sln
+++ b/Grabby.sln
@@ -1,5 +1,13 @@
Microsoft Visual Studio Solution File, Format Version 8.00
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Grabby", "Grabby.vcproj", "{700E5DD7-DD00-46D9-92DF-2C132940F53D}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Grabby", "Grabby\Grabby.vcproj", "{700E5DD7-DD00-46D9-92DF-2C132940F53D}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GrbyCfg", "GrbyCfg\GrbyCfg.vcproj", "{BBB3E933-71CC-4EF7-BF56-52371B9AF938}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CommonCfg", "CommonCfg\CommonCfg.vcproj", "{61829180-05C1-4865-B9C3-FEB983AD6997}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
@@ -13,6 +21,14 @@ Global
{700E5DD7-DD00-46D9-92DF-2C132940F53D}.Debug.Build.0 = Debug|Win32
{700E5DD7-DD00-46D9-92DF-2C132940F53D}.Release.ActiveCfg = Release|Win32
{700E5DD7-DD00-46D9-92DF-2C132940F53D}.Release.Build.0 = Release|Win32
+ {BBB3E933-71CC-4EF7-BF56-52371B9AF938}.Debug.ActiveCfg = Debug|Win32
+ {BBB3E933-71CC-4EF7-BF56-52371B9AF938}.Debug.Build.0 = Debug|Win32
+ {BBB3E933-71CC-4EF7-BF56-52371B9AF938}.Release.ActiveCfg = Release|Win32
+ {BBB3E933-71CC-4EF7-BF56-52371B9AF938}.Release.Build.0 = Release|Win32
+ {61829180-05C1-4865-B9C3-FEB983AD6997}.Debug.ActiveCfg = Debug|Win32
+ {61829180-05C1-4865-B9C3-FEB983AD6997}.Debug.Build.0 = Debug|Win32
+ {61829180-05C1-4865-B9C3-FEB983AD6997}.Release.ActiveCfg = Release|Win32
+ {61829180-05C1-4865-B9C3-FEB983AD6997}.Release.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection
diff --git a/Bitmap.c b/Grabby/Bitmap.c
similarity index 100%
rename from Bitmap.c
rename to Grabby/Bitmap.c
diff --git a/Bitmap.h b/Grabby/Bitmap.h
similarity index 100%
rename from Bitmap.h
rename to Grabby/Bitmap.h
diff --git a/Grabby.c b/Grabby/Grabby.c
similarity index 80%
rename from Grabby.c
rename to Grabby/Grabby.c
index d44d8b9..c533d22 100644
--- a/Grabby.c
+++ b/Grabby/Grabby.c
@@ -5,6 +5,7 @@
#include "Bitmap.h"
#include "Overlay.h"
#include "Grabby.h"
+#include
/**
* Print an error with a given context before bailing out
@@ -43,7 +44,7 @@ void Die(LPTSTR lpContextMsg)
_T("Catastrophic Error"),
MB_OK | MB_ICONERROR);
} else {
- _sntprintf(lpszFullMsg, iFullLen, lpszFmt, lpContextMsg, lpszError);
+ StringCchPrintf(lpszFullMsg, iFullLen, lpszFmt, lpContextMsg, lpszError);
MessageBox(NULL,
lpszFullMsg,
_T("Fatal Error"),
@@ -62,16 +63,24 @@ int APIENTRY _tWinMain(HINSTANCE hInstance,
SYSTEMTIME stNow = {0};
RECT capRegion = {0};
Screen *scrn = CreateScreen();
-#define OUTPUT_DIR_LEN 96
- TCHAR szOutputDir[OUTPUT_DIR_LEN + 1];
-#define FULL_OUT_PATH_LEN OUTPUT_DIR_LEN + 24
- TCHAR szFullOutPath[FULL_OUT_PATH_LEN + 1];
+ Config *cfg;
+ TCHAR szFullOutPath[MAX_PATH];
HRESULT hRes;
if (!scrn) {
Die("Couldn't get screen information");
}
+ cfg = GetConfig();
+ if (!cfg) {
+ Die("Couldn't get config");
+ }
+
+ if (!PathFileExists(cfg->ScreenshotDir) &&
+ !CreateDirectory(cfg->ScreenshotDir, NULL)) {
+ Die("Couldn't create configured folder");
+ }
+
switch (CreateOverlay(hInstance, scrn)) {
case -1:
Die("Couldn't create overlay window");
@@ -86,16 +95,12 @@ int APIENTRY _tWinMain(HINSTANCE hInstance,
GetChosenRect(&capRegion);
- if (!GetTempPath(OUTPUT_DIR_LEN, szOutputDir)) {
- Die("Couldn't get temporary directory");
- }
-
GetLocalTime(&stNow);
hRes = StringCchPrintf(szFullOutPath,
- FULL_OUT_PATH_LEN,
- "%s\\scrn_%04d%02d%02d_%02d%02d%02d.bmp",
- szOutputDir,
+ MAX_PATH,
+ "%s\\Screenshot at %04d-%02d-%02d %02d.%02d.%02d.bmp",
+ cfg->ScreenshotDir,
stNow.wYear,
stNow.wMonth,
stNow.wDay,
diff --git a/Grabby.h b/Grabby/Grabby.h
similarity index 100%
rename from Grabby.h
rename to Grabby/Grabby.h
diff --git a/Grabby.ico b/Grabby/Grabby.ico
similarity index 100%
rename from Grabby.ico
rename to Grabby/Grabby.ico
diff --git a/Grabby.rc b/Grabby/Grabby.rc
similarity index 55%
rename from Grabby.rc
rename to Grabby/Grabby.rc
index 57d852f..d973872 100644
--- a/Grabby.rc
+++ b/Grabby/Grabby.rc
@@ -1,4 +1,4 @@
-//Microsoft Visual C++ generated resource script.
+// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
@@ -10,12 +10,58 @@
#define APSTUDIO_HIDDEN_SYMBOLS
#include "windows.h"
#undef APSTUDIO_HIDDEN_SYMBOLS
+
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+// Japanese resources
+
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN)
-LANGUAGE 17, 1
+#ifdef _WIN32
+LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
#pragma code_page(932)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "#include ""windows.h""\r\n"
+ "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // Japanese resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
@@ -24,95 +70,10 @@ LANGUAGE 17, 1
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
-
-IDI_GRABBY ICON "Grabby.ico"
-IDI_SMALL ICON "small.ico"
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Menu
-//
-
-IDC_GRABBY MENU
-BEGIN
- POPUP "&File"
- BEGIN
- MENUITEM "E&xit", IDM_EXIT
- END
- POPUP "&Help"
- BEGIN
- MENUITEM "&About ...", IDM_ABOUT
- END
-END
-
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Accelerator
-//
-
-IDC_GRABBY ACCELERATORS
-BEGIN
- "?", IDM_ABOUT, ASCII, ALT
- "/", IDM_ABOUT, ASCII, ALT
-END
-
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Dialog
-//
-
-IDD_ABOUTBOX DIALOG 22, 17, 230, 75
-STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
-CAPTION "About"
-FONT 9, "System"
-BEGIN
- ICON IDI_GRABBY,IDC_MYICON,14,9,16,16
- LTEXT "Grabby Version 1.0",IDC_STATIC,49,10,119,8,SS_NOPREFIX
- LTEXT "Copyright (C) 2024",IDC_STATIC,49,20,119,8
- DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
-END
-
-
-#ifdef APSTUDIO_INVOKED
-/////////////////////////////////////////////////////////////////////////////
-//
-// TEXTINCLUDE
-//
-1 TEXTINCLUDE
-BEGIN
- "resource.h\0"
-END
-
-2 TEXTINCLUDE
-BEGIN
- "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
- "#include ""windows.h""\r\n"
- "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
- "\0"
-END
-
-3 TEXTINCLUDE
-BEGIN
- "\r\n"
- "\0"
-END
-
-#endif // APSTUDIO_INVOKED
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// String Table
-//
-
-STRINGTABLE
-BEGIN
- IDC_GRABBY "GRABBY"
- IDS_APP_TITLE "Grabby"
-END
-
-#endif
+IDI_GRABBY ICON "Grabby.ico"
+IDI_SMALL ICON "small.ico"
+IDI_ICON1 ICON "icon1.ico"
+#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
@@ -126,3 +87,4 @@ END
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
+
diff --git a/Grabby.vcproj b/Grabby/Grabby.vcproj
similarity index 82%
rename from Grabby.vcproj
rename to Grabby/Grabby.vcproj
index be2f988..a1ba43c 100644
--- a/Grabby.vcproj
+++ b/Grabby/Grabby.vcproj
@@ -19,6 +19,7 @@
+ DebugInformationFormat="3"
+ CompileAs="1"/>
+
+
+
+ RelativePath="..\ReadMe.txt">
diff --git a/Overlay.c b/Grabby/Overlay.c
similarity index 100%
rename from Overlay.c
rename to Grabby/Overlay.c
diff --git a/Overlay.h b/Grabby/Overlay.h
similarity index 100%
rename from Overlay.h
rename to Grabby/Overlay.h
diff --git a/Grabby/icon1.ico b/Grabby/icon1.ico
new file mode 100644
index 0000000..c2746b0
Binary files /dev/null and b/Grabby/icon1.ico differ
diff --git a/Grabby/resource.h b/Grabby/resource.h
new file mode 100644
index 0000000..da08ba3
--- /dev/null
+++ b/Grabby/resource.h
@@ -0,0 +1,36 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Grabby.rc
+//
+#define IDC_MYICON 2
+#define IDD_GRABBY_DIALOG 102
+#define IDS_APP_TITLE 103
+#define IDM_ABOUT 104
+#define IDM_EXIT 105
+#define IDI_GRABBY 107
+#define IDI_SMALL 108
+#define IDC_GRABBY 109
+#define IDR_MAINFRAME 128
+#define IDI_ICON1 129
+#define IDC_TABVIEW 1000
+#define IDC_APPLY 1001
+#define IDC_EDIT1 1004
+#define IDC_BUTTON1 1005
+#define IDC_CHECK1 1006
+#define IDC_URLINPUT 1007
+#define IDC_ENCODEFILE 1008
+#define IDC_COMBO1 1009
+#define IDC_UPLOADMETHOD 1009
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 132
+#define _APS_NEXT_COMMAND_VALUE 32771
+#define _APS_NEXT_CONTROL_VALUE 1010
+#define _APS_NEXT_SYMED_VALUE 110
+#endif
+#endif
diff --git a/small.ico b/Grabby/small.ico
similarity index 100%
rename from small.ico
rename to Grabby/small.ico
diff --git a/stdafx.c b/Grabby/stdafx.c
similarity index 100%
rename from stdafx.c
rename to Grabby/stdafx.c
diff --git a/stdafx.h b/Grabby/stdafx.h
similarity index 87%
rename from stdafx.h
rename to Grabby/stdafx.h
index 6319016..e73048a 100644
--- a/stdafx.h
+++ b/Grabby/stdafx.h
@@ -9,6 +9,7 @@
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include
+#include
// C RunTime Header Files
#include
#include
@@ -16,4 +17,4 @@
#include
#include
-#include
+#include
\ No newline at end of file
diff --git a/GrbyCfg/Dialog.c b/GrbyCfg/Dialog.c
new file mode 100644
index 0000000..8d6feed
--- /dev/null
+++ b/GrbyCfg/Dialog.c
@@ -0,0 +1,11 @@
+#define _IS_DIALOG_C_
+#include "stdafx.h"
+#include "Dialog.h"
+
+
+void
+InitUserMsgs(void)
+{
+ DEFINE_USER_MSG(WMU_SAVE_CONFIG);
+ DEFINE_USER_MSG(WMU_SETTING_CHANGED);
+}
\ No newline at end of file
diff --git a/GrbyCfg/Dialog.h b/GrbyCfg/Dialog.h
new file mode 100644
index 0000000..41e3cfc
--- /dev/null
+++ b/GrbyCfg/Dialog.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#define _DLG_MSG_SUFFIX "{990f4b34-579a-4fbc-a5b2-08f46ef5e9c5}"
+#define USER_MSG_NAME(name) _T( #name _DLG_MSG_SUFFIX )
+#define DEFINE_USER_MSG(name) {\
+ LPCTSTR name##_MSG = USER_MSG_NAME(name); \
+ name = RegisterWindowMessage(name##_MSG); \
+}
+#define DECLARE_USER_MSG(name) \
+ UINT name;
+
+/* --- */
+
+DECLARE_USER_MSG(WMU_SAVE_CONFIG)
+DECLARE_USER_MSG(WMU_SETTING_CHANGED)
+
+void InitUserMsgs(void);
+
+#define ChangedSetting(hwnd) \
+ SendMessage(hwnd, \
+ WMU_SETTING_CHANGED, \
+ 0, 0)
+
+#define IsChecked(ref) \
+ (IsDlgButtonChecked(hDlg, ref) == BST_CHECKED)
\ No newline at end of file
diff --git a/GrbyCfg/General.h b/GrbyCfg/General.h
new file mode 100644
index 0000000..c6d3a7b
--- /dev/null
+++ b/GrbyCfg/General.h
@@ -0,0 +1,4 @@
+#pragma once
+
+// Define the dialog proc
+DEF_DLGPROC(GeneralTab);
\ No newline at end of file
diff --git a/GrbyCfg/GeneralCfg.c b/GrbyCfg/GeneralCfg.c
new file mode 100644
index 0000000..cd07ba1
--- /dev/null
+++ b/GrbyCfg/GeneralCfg.c
@@ -0,0 +1,182 @@
+/* General configuration dialog */
+
+#include "stdafx.h"
+#include "GrbyCfg.h"
+
+int CALLBACK
+ScreenshotBrowseCallback(HWND hWnd,
+ UINT uMsg,
+ LPARAM lParam,
+ LPARAM lpData)
+{
+ if (uMsg == BFFM_INITIALIZED && lpData) {
+ // Set root dir
+ SendMessage(hWnd,
+ BFFM_SETSELECTION,
+ 1,
+ lpData);
+ }
+
+ return 0;
+}
+
+INT_PTR CALLBACK
+GeneralTab_DlgProc(HWND hDlg,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ static Config *cfg = NULL;
+ static HWND hStoreDir = NULL,
+ hCustomShortcut = NULL,
+ hParentDlg = NULL;
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ hParentDlg = GetParent(hDlg);
+
+ cfg = GetConfig();
+ if (!cfg) {
+ // TODO: better error management
+ // (probably SendMessage upstream)
+ MessageBox(hDlg,
+ _T("Can't get config! (TODO: error msg)"),
+ _T("Config Error"),
+ MB_OK | MB_ICONERROR);
+ DestroyWindow(hDlg);
+ }
+
+ hStoreDir = GetDlgItem(hDlg, IDC_SCRNDIR);
+ if (!hStoreDir) {
+ // TODO: error management x_x
+ return FALSE;
+ }
+
+ if (strlen(cfg->ScreenshotDir) > 0) {
+ // Set the screenshot dir, if we have it
+ SendMessage(hStoreDir,
+ EM_SETLIMITTEXT,
+ (WPARAM)MAX_PATH,
+ 0);
+ SetWindowText(hStoreDir,
+ cfg->ScreenshotDir);
+ }
+
+ if (cfg->KeepScreenshots) {
+ // Keep screenshots after upload?
+ CheckDlgButton(hDlg,
+ IDC_KEEPIMGS,
+ BST_CHECKED);
+ }
+
+ if (cfg->HelperStartup) {
+ // Launch helper on startup?
+ CheckDlgButton(hDlg,
+ IDC_LAUNCHHELPER,
+ BST_CHECKED);
+ }
+
+ hCustomShortcut = GetDlgItem(hDlg,
+ IDC_CUSTOMKEY);
+ if (!hCustomShortcut) {
+ // TODO: error management orz
+ return FALSE;
+ }
+
+ // Enable the shortcut input if Custom is set
+ EnableWindow(hCustomShortcut,
+ (cfg->HelperShortcut == HSC_CUSTOM));
+
+ // Also, check the correct radio box. This is hacky and
+ // expects IDC_RDMAC/WIN/CUSTOM is not changed, so uhhhh
+ // please don't change that assumption? ^_^;;
+ CheckDlgButton(hDlg,
+ IDC_RDMAC + min(2, cfg->HelperShortcut),
+ BST_CHECKED);
+ break;
+ case WM_NOTIFY:
+ // TODO
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_LAUNCHHELPER:
+ cfg->HelperStartup =
+ (IsDlgButtonChecked(hDlg, IDC_LAUNCHHELPER) == BST_CHECKED);
+ ChangedSetting(hParentDlg);
+ break;
+
+ case IDC_KEEPIMGS:
+ cfg->KeepScreenshots =
+ (IsDlgButtonChecked(hDlg, IDC_KEEPIMGS) == BST_CHECKED);
+ ChangedSetting(hParentDlg);
+ break;
+
+ case IDC_RDMAC:
+ case IDC_RDWIN:
+ case IDC_RDCUSTOM:
+ // Generates the correct enum val a la GetConfig.
+ cfg->HelperShortcut = IsChecked(IDC_RDMAC) ? HSC_MACOS :
+ (IsChecked(IDC_RDWIN) ? HSC_WIN10 : HSC_CUSTOM);
+ // Enable/Disable the Custom Shortcut control as needed
+ EnableWindow(hCustomShortcut,
+ (cfg->HelperShortcut == HSC_CUSTOM));
+ ChangedSetting(hParentDlg);
+ break;
+
+ case IDC_SCRDIRBROWSE: {
+ LPITEMIDLIST lPidl;
+ BROWSEINFO bi = {0};
+
+ bi.hwndOwner = hDlg;
+ bi.lParam = (LPARAM)cfg->ScreenshotDir;
+ bi.lpfn = ScreenshotBrowseCallback;
+ bi.lpszTitle = _T("Select Screenshot Folder");
+
+ lPidl = SHBrowseForFolder(&bi);
+ if (lPidl == NULL) {
+ // Cancelled
+ break;
+ }
+
+ SHGetPathFromIDList(lPidl,
+ cfg->ScreenshotDir);
+
+ // We don't need the PIDL anymore, so free it
+ {
+ LPMALLOC pMalloc;
+ if (SHGetMalloc(&pMalloc) == NOERROR) {
+ IMalloc_Free(pMalloc, lPidl);
+ IMalloc_Release(pMalloc);
+ }
+ }
+
+ // Update the edit control
+ SetWindowText(hStoreDir,
+ cfg->ScreenshotDir);
+
+ ChangedSetting(hParentDlg);
+ }
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ break;
+ default:
+ // User-defined messages
+ if (uMsg == WMU_SAVE_CONFIG) {
+ GetWindowText(hStoreDir,
+ cfg->ScreenshotDir,
+ MAX_PATH - 1);
+ if (!SaveConfig(cfg)) {
+ // TODO: error management
+ OutputDebugString(_T("no save x_x"));
+ }
+ } else {
+ // Not a message we care about
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
\ No newline at end of file
diff --git a/GrbyCfg/GrbyCfg.c b/GrbyCfg/GrbyCfg.c
new file mode 100644
index 0000000..e4149a4
--- /dev/null
+++ b/GrbyCfg/GrbyCfg.c
@@ -0,0 +1,272 @@
+#include "stdafx.h"
+#include "GrbyCfg.h"
+#define MAX_LOADSTRING 100
+#include
+
+INT_PTR CALLBACK ConfigDlgProc(HWND, UINT, WPARAM, LPARAM);
+
+/**
+ * Print an error with a given context before bailing out
+ */
+void Die(LPTSTR lpContextMsg)
+{
+ LPCTSTR lpszFmt = "%s: %s";
+ int iFullLen;
+ LPTSTR lpszError = NULL,
+ lpszFullMsg = NULL;
+
+ // The whole point of this is to print an actual message, so get the
+ // actual message
+ if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ GetLastError(),
+ 0,
+ (LPTSTR) &lpszError,
+ 0,
+ NULL))
+ {
+ if (lpszError != NULL) LocalFree(lpszError);
+ lpszError = _T("unknown error");
+ }
+
+ // Create our WMU_* messages
+ InitUserMsgs();
+
+ iFullLen = _sctprintf(lpszFmt, lpContextMsg, lpszError);
+ lpszFullMsg = (LPTSTR) LocalAlloc(LPTR, sizeof(TCHAR) * (iFullLen + 1));
+
+ if (lpszFullMsg == NULL) {
+ // I'm honestly gonna guess if we get here, we're out of memory.
+ // So let's print what we have and bail
+ MessageBox(NULL,
+ lpContextMsg,
+ _T("Catastrophic Error"),
+ MB_OK | MB_ICONERROR);
+ } else {
+ _sntprintf(lpszFullMsg, iFullLen, lpszFmt, lpContextMsg, lpszError);
+ MessageBox(NULL,
+ lpszFullMsg,
+ _T("Fatal Error"),
+ MB_OK | MB_ICONERROR);
+ }
+
+ ExitProcess(1);
+}
+
+HINSTANCE g_hInstance = NULL;
+
+int APIENTRY _tWinMain(HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPTSTR lpCmdLine,
+ int nCmdShow)
+{
+ BOOL bRet;
+ MSG msg;
+ HWND hDlg;
+ INITCOMMONCONTROLSEX lpInitCtrls = {0};
+ HRESULT hrRet;
+
+ g_hInstance = hInstance;
+
+ // Required for tab control
+ lpInitCtrls.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ lpInitCtrls.dwICC = ICC_WIN95_CLASSES;
+ if (!InitCommonControlsEx(&lpInitCtrls)) {
+ MessageBox(NULL,
+ _T("Couldn't initialize comctl32! Not sure why..."),
+ _T("Ooh, rare bug!"),
+ MB_OK | MB_ICONERROR);
+ return 1;
+ }
+
+ // Required for folder browse dialog
+ hrRet = CoInitializeEx(NULL,
+ COINIT_APARTMENTTHREADED);
+ if (hrRet != S_OK) {
+ MessageBox(NULL,
+ _T("Couldn't initialize COM!?"),
+ _T("dot com bust u_u"),
+ MB_OK | MB_ICONERROR);
+ return 1;
+ }
+
+ hDlg = CreateDialog(hInstance,
+ MAKEINTRESOURCE(IDD_CONFIGDIALOG),
+ NULL,
+ ConfigDlgProc);
+
+ if (!hDlg) {
+ Die("CreateDialog failed");
+ }
+
+ ShowWindow(hDlg, nCmdShow);
+
+ while ((bRet = GetMessage(&msg, hDlg, 0, 0)) != FALSE) {
+ if (bRet == -1) {
+ // TODO: error handling
+ break;
+ } else {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ return (int)msg.wParam;
+}
+
+// Helper macro to create a new text (i.e., no icon) tab for config
+// items.
+//
+// NOTE: This is written to use Tab_DlgProc for dlgProc. This
+// assumes that tabs will always be valid (enough) for a function
+// identifier.
+#define NEW_TEXTTAB(name, res) {{ \
+ TCIF_TEXT, \
+ 0, 0, \
+ _T(#name), \
+ sizeof(_T(#name)) / sizeof(TCHAR), \
+ 0, 0 \
+ }, \
+ res, \
+ name##Tab_DlgProc, \
+ NULL \
+}
+
+INT_PTR CALLBACK
+ConfigDlgProc(HWND hWnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ // Define the tab metadata
+ static struct {
+ TCITEM tciTab;
+ int child;
+ DLGPROC dlgProc;
+ HWND hWnd;
+ } tabs[] = {
+ NEW_TEXTTAB(General,
+ IDD_GENCONFIG),
+ NEW_TEXTTAB(Upload,
+ IDD_UPLOADCONFIG)
+ };
+ BOOL bClose = FALSE;
+
+ const int tabCount = (sizeof(tabs) / sizeof(tabs[0]));
+
+ switch (uMsg) {
+ case WM_INITDIALOG: {
+ RECT rcTab;
+ int i;
+ HWND hTabCtrl = GetDlgItem(hWnd, IDC_TABVIEW);
+
+ if (!hTabCtrl) {
+ Die("Couldn't get tab control");
+ }
+
+ for (i = 0; i < tabCount; i++) {
+ int iResult = TabCtrl_InsertItem(
+ hTabCtrl,
+ i,
+ &tabs[i].tciTab);
+ if (iResult == -1) {
+ Die("Couldn't create tabs");
+ }
+
+ // Attach the child dialog
+ tabs[i].hWnd = CreateDialog(g_hInstance,
+ MAKEINTRESOURCE(tabs[i].child),
+ hWnd,
+ tabs[i].dlgProc);
+ if (!tabs[i].hWnd) {
+ Die("Couldn't create child tab");
+ }
+ }
+
+ // This has to happen after the tabs are inserted,
+ // or else we get the wrong client area (tabs don't
+ // exist yet!)
+ GetWindowRect(hTabCtrl, &rcTab);
+ MapWindowPoints(NULL, hWnd, (LPPOINT)&rcTab, 2);
+ TabCtrl_AdjustRect(hTabCtrl, FALSE, &rcTab);
+
+ for (i = 0; i < tabCount; i++) {
+ SetWindowPos(tabs[i].hWnd,
+ HWND_TOP,
+ rcTab.left,
+ rcTab.top,
+ rcTab.right - rcTab.left,
+ rcTab.bottom - rcTab.top,
+ (i == 0) ? SWP_SHOWWINDOW : 0);
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDCANCEL:
+ DestroyWindow(hWnd);
+ break;
+ case IDOK:
+ bClose = TRUE;
+ /* PASSTHRU */
+ case IDC_APPLY: {
+ int i;
+ for (i = 0; i < tabCount; i++) {
+ SendMessage(tabs[i].hWnd,
+ WMU_SAVE_CONFIG,
+ 0, 0);
+ }
+
+ EnableWindow(GetDlgItem(hWnd, IDC_APPLY), FALSE);
+ // Handle OK button (Apply + Close)
+ if (bClose) SendMessage(hWnd,
+ WM_CLOSE,
+ 0,
+ 0);
+ }
+ break;
+ default:
+ // TODO: other buttons
+ break;
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hWnd);
+ break;
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR pnmh = (LPNMHDR)lParam;
+ if (pnmh->code == TCN_SELCHANGE) {
+ int idx = TabCtrl_GetCurSel(pnmh->hwndFrom);
+ if (idx < tabCount) {
+ int i;
+ for (i = 0; i < tabCount; i++) {
+ if (tabs[i].hWnd) {
+ // If it's the child we want, show it.
+ // If not, hide it.
+ ShowWindow(tabs[i].hWnd,
+ (i == idx) ? SW_SHOW : SW_HIDE);
+ }
+ }
+ }
+ // If idx >= tabCount, ignore the message
+ }
+ }
+ break;
+ default:
+ // User-defined messages
+ if (uMsg == WMU_SETTING_CHANGED) {
+ EnableWindow(GetDlgItem(hWnd, IDC_APPLY), TRUE);
+ } else {
+ // Not our message, ignore
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
\ No newline at end of file
diff --git a/GrbyCfg/GrbyCfg.h b/GrbyCfg/GrbyCfg.h
new file mode 100644
index 0000000..301a985
--- /dev/null
+++ b/GrbyCfg/GrbyCfg.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "resource.h"
+
+// Define the config tabs
+INT_PTR CALLBACK
+GeneralTab_DlgProc(HWND hDlg,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam);
+
+INT_PTR CALLBACK
+UploadTab_DlgProc(HWND hDlg,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam);
\ No newline at end of file
diff --git a/GrbyCfg/GrbyCfg.ico b/GrbyCfg/GrbyCfg.ico
new file mode 100644
index 0000000..d551aa3
Binary files /dev/null and b/GrbyCfg/GrbyCfg.ico differ
diff --git a/GrbyCfg/GrbyCfg.rc b/GrbyCfg/GrbyCfg.rc
new file mode 100644
index 0000000..db7f070
--- /dev/null
+++ b/GrbyCfg/GrbyCfg.rc
@@ -0,0 +1,270 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Japanese resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN)
+#ifdef _WIN32
+LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
+#pragma code_page(932)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_GRBYCFG ICON "GrbyCfg.ico"
+IDI_SMALL ICON "small.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDC_GRBYCFG MENU
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "E&xit", IDM_EXIT
+ END
+ POPUP "&Help"
+ BEGIN
+ MENUITEM "&About ...", IDM_ABOUT
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+IDC_GRBYCFG ACCELERATORS
+BEGIN
+ "?", IDM_ABOUT, ASCII, ALT
+ "/", IDM_ABOUT, ASCII, ALT
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "#include ""windows.h""\r\n"
+ "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_APP_TITLE "GrbyCfg"
+ IDC_GRBYCFG "GRBYCFG"
+END
+
+#endif // Japanese resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_GENCONFIG DIALOGEX 0, 0, 210, 206
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_BORDER
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ GROUPBOX "Screenshot Folder",IDC_STATIC,7,7,196,52,WS_GROUP
+ EDITTEXT IDC_SCRNDIR,13,19,184,14,ES_AUTOHSCROLL
+ PUSHBUTTON "Browse...",IDC_SCRDIRBROWSE,147,39,50,14
+ CONTROL "Keep screenshots after upload",IDC_KEEPIMGS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,13,39,114,14
+ GROUPBOX "Launch Helper",IDC_STATIC,7,65,196,57
+ LTEXT "Grabby is intended to be launched via a keyboard shortcut using a separate helper program. A basic helper is bundled.",
+ IDC_STATIC,13,77,184,25
+ CONTROL "Launch Helper on Startup",IDC_LAUNCHHELPER,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,13,106,184,10
+ GROUPBOX "Keyboard Shortcut",IDC_STATIC,7,128,196,71
+ LTEXT "When the Launch Helper is running, take a screenshot with:",
+ IDC_STATIC,13,140,184,16
+ CONTROL "Win+Shift+4 (Mac)",IDC_RDMAC,"Button",
+ BS_AUTORADIOBUTTON | WS_GROUP,13,163,77,10
+ CONTROL "Win+Shift+S (Win10)",IDC_RDWIN,"Button",
+ BS_AUTORADIOBUTTON,96,163,101,10
+ CONTROL "Custom",IDC_RDCUSTOM,"Button",BS_AUTORADIOBUTTON,13,175,
+ 77,14
+ EDITTEXT IDC_CUSTOMKEY,96,175,101,14,ES_AUTOHSCROLL | WS_GROUP
+END
+
+IDD_UPLOADCONFIG DIALOGEX 0, 0, 210, 154
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_BORDER
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ GROUPBOX "Uploader",IDC_STATIC,7,7,196,88
+ LTEXT "Enter the URL to use. Use ""%f"" to represent the filename, if it should be included in the URL.",
+ IDC_STATIC,11,19,186,17
+ EDITTEXT IDC_URLINPUT,13,43,184,12,ES_AUTOHSCROLL
+ CONTROL "Encode filename in URL (for query parameters)",
+ IDC_ENCODEFILE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,
+ 61,166,10
+ LTEXT "HTTP method to use:",IDC_STATIC,13,77,68,12,
+ SS_CENTERIMAGE
+ COMBOBOX IDC_UPLOADMETHOD,87,77,110,30,CBS_DROPDOWNLIST |
+ CBS_SORT | CBS_UPPERCASE | WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "Authentication",IDC_STATIC,7,101,196,40
+END
+
+IDD_CONFIGDIALOG DIALOGEX 0, 0, 234, 254
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "Options"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,65,233,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,121,233,50,14
+ CONTROL "",IDC_TABVIEW,"SysTabControl32",0x0,7,7,220,220
+ PUSHBUTTON "Apply",IDC_APPLY,177,233,50,14,WS_DISABLED
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_GENCONFIG, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 203
+ VERTGUIDE, 13
+ VERTGUIDE, 90
+ VERTGUIDE, 96
+ VERTGUIDE, 197
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 199
+ HORZGUIDE, 19
+ HORZGUIDE, 33
+ HORZGUIDE, 39
+ HORZGUIDE, 53
+ HORZGUIDE, 59
+ HORZGUIDE, 65
+ HORZGUIDE, 77
+ HORZGUIDE, 102
+ HORZGUIDE, 106
+ HORZGUIDE, 122
+ HORZGUIDE, 128
+ HORZGUIDE, 140
+ HORZGUIDE, 156
+ HORZGUIDE, 163
+ HORZGUIDE, 173
+ HORZGUIDE, 193
+ HORZGUIDE, 199
+ END
+
+ IDD_UPLOADCONFIG, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 203
+ VERTGUIDE, 13
+ VERTGUIDE, 81
+ VERTGUIDE, 87
+ VERTGUIDE, 197
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 147
+ HORZGUIDE, 19
+ HORZGUIDE, 36
+ HORZGUIDE, 43
+ HORZGUIDE, 61
+ HORZGUIDE, 71
+ HORZGUIDE, 77
+ HORZGUIDE, 89
+ HORZGUIDE, 95
+ HORZGUIDE, 101
+ END
+
+ IDD_CONFIGDIALOG, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 227
+ VERTGUIDE, 115
+ VERTGUIDE, 121
+ VERTGUIDE, 171
+ VERTGUIDE, 177
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 247
+ HORZGUIDE, 227
+ HORZGUIDE, 233
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/GrbyCfg/GrbyCfg.vcproj b/GrbyCfg/GrbyCfg.vcproj
new file mode 100644
index 0000000..bb65d67
--- /dev/null
+++ b/GrbyCfg/GrbyCfg.vcproj
@@ -0,0 +1,188 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GrbyCfg/ReadMe.txt b/GrbyCfg/ReadMe.txt
new file mode 100644
index 0000000..fc2c35e
--- /dev/null
+++ b/GrbyCfg/ReadMe.txt
@@ -0,0 +1,53 @@
+========================================================================
+ WIN32 APPLICATION : GrbyCfg Project Overview
+========================================================================
+
+AppWizard has created this GrbyCfg application for you.
+This file contains a summary of what you will find in each of the files that
+make up your GrbyCfg application.
+
+
+GrbyCfg.vcproj
+ This is the main project file for VC++ projects generated using an Application Wizard.
+ It contains information about the version of Visual C++ that generated the file, and
+ information about the platforms, configurations, and project features selected with the
+ Application Wizard.
+
+GrbyCfg.cpp
+ This is the main application source file.
+
+/////////////////////////////////////////////////////////////////////////////
+AppWizard has created the following resources:
+
+GrbyCfg.rc
+ This is a listing of all of the Microsoft Windows resources that the
+ program uses. It includes the icons, bitmaps, and cursors that are stored
+ in the RES subdirectory. This file can be directly edited in Microsoft
+ Visual C++.
+
+Resource.h
+ This is the standard header file, which defines new resource IDs.
+ Microsoft Visual C++ reads and updates this file.
+GrbyCfg.ico
+ This is an icon file, which is used as the application's icon (32x32).
+ This icon is included by the main resource file GrbyCfg.rc.
+
+small.ico
+ This is an icon file, which contains a smaller version (16x16)
+ of the application's icon. This icon is included by the main resource
+ file GrbyCfg.rc.
+
+/////////////////////////////////////////////////////////////////////////////
+Other standard files:
+
+StdAfx.h, StdAfx.cpp
+ These files are used to build a precompiled header (PCH) file
+ named GrbyCfg.pch and a precompiled types file named StdAfx.obj.
+
+/////////////////////////////////////////////////////////////////////////////
+Other notes:
+
+AppWizard uses "TODO:" comments to indicate parts of the source code you
+should add to or customize.
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/GrbyCfg/UploadCfg.c b/GrbyCfg/UploadCfg.c
new file mode 100644
index 0000000..b0a2fb6
--- /dev/null
+++ b/GrbyCfg/UploadCfg.c
@@ -0,0 +1,11 @@
+#include "stdafx.h"
+
+INT_PTR CALLBACK
+UploadTab_DlgProc(HWND hDlg,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ // TODO: anything
+ return FALSE;
+}
\ No newline at end of file
diff --git a/GrbyCfg/Uploader.h b/GrbyCfg/Uploader.h
new file mode 100644
index 0000000..e69de29
diff --git a/GrbyCfg/resource.h b/GrbyCfg/resource.h
new file mode 100644
index 0000000..ce6dc24
--- /dev/null
+++ b/GrbyCfg/resource.h
@@ -0,0 +1,42 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by GrbyCfg.rc
+//
+#define IDC_MYICON 2
+#define IDD_CONFIGDIALOG 9
+#define IDD_GRBYCFG_DIALOG 102
+#define IDS_APP_TITLE 103
+#define IDM_ABOUT 104
+#define IDM_EXIT 105
+#define IDD_GENCONFIG 106
+#define IDI_GRBYCFG 107
+#define IDI_SMALL 108
+#define IDC_GRBYCFG 109
+#define IDR_MAINFRAME 128
+#define IDD_UPLOADCONFIG 130
+#define IDC_TABVIEW 1000
+#define IDC_APPLY 1001
+#define IDC_SCRNDIR 1004
+#define IDC_SCRDIRBROWSE 1005
+#define IDC_KEEPIMGS 1006
+#define IDC_URLINPUT 1007
+#define IDC_LAUNCHHELPER 1007
+#define IDC_ENCODEFILE 1008
+#define IDC_RDMAC 1008
+#define IDC_UPLOADMETHOD 1009
+#define IDC_RDWIN 1009
+#define IDC_RDCUSTOM 1010
+#define IDC_CUSTOMKEY 1011
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 130
+#define _APS_NEXT_COMMAND_VALUE 32771
+#define _APS_NEXT_CONTROL_VALUE 1012
+#define _APS_NEXT_SYMED_VALUE 110
+#endif
+#endif
diff --git a/GrbyCfg/small.ico b/GrbyCfg/small.ico
new file mode 100644
index 0000000..d551aa3
Binary files /dev/null and b/GrbyCfg/small.ico differ
diff --git a/GrbyCfg/stdafx.c b/GrbyCfg/stdafx.c
new file mode 100644
index 0000000..f840850
--- /dev/null
+++ b/GrbyCfg/stdafx.c
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// GrbyCfg.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/GrbyCfg/stdafx.h b/GrbyCfg/stdafx.h
new file mode 100644
index 0000000..7423c8d
--- /dev/null
+++ b/GrbyCfg/stdafx.h
@@ -0,0 +1,26 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+// Windows Header Files:
+#include
+#include
+#include
+#define _WIN32_DCOM
+#define COBJMACROS
+#define CINTERFACE
+#include
+#include
+// C RunTime Header Files
+#include
+#include
+#include
+
+#include
+#include "Dialog.h"
+#include "resource.h"
\ No newline at end of file
diff --git a/Resource.h b/Resource.h
deleted file mode 100644
index 6bcd5f7..0000000
--- a/Resource.h
+++ /dev/null
@@ -1,29 +0,0 @@
-//{{NO_DEPENDENCIES}}
-// Microsoft Visual C++ generated include file.
-// Used by Grabby.rc
-//
-
-#define IDS_APP_TITLE 103
-
-#define IDR_MAINFRAME 128
-#define IDD_GRABBY_DIALOG 102
-#define IDD_ABOUTBOX 103
-#define IDM_ABOUT 104
-#define IDM_EXIT 105
-#define IDI_GRABBY 107
-#define IDI_SMALL 108
-#define IDC_GRABBY 109
-#define IDC_MYICON 2
-#define IDC_STATIC -1
-// Next default values for new objects
-//
-#ifdef APSTUDIO_INVOKED
-#ifndef APSTUDIO_READONLY_SYMBOLS
-
-#define _APS_NO_MFC 130
-#define _APS_NEXT_RESOURCE_VALUE 129
-#define _APS_NEXT_COMMAND_VALUE 32771
-#define _APS_NEXT_CONTROL_VALUE 1000
-#define _APS_NEXT_SYMED_VALUE 110
-#endif
-#endif