/** * MojoSetup; a portable, flexible installation application. * * Please see the file LICENSE.txt in the source's root directory. * * This file written by Ryan C. Gordon. */ #if PLATFORM_WINDOWS #include "platform.h" #include "gui.h" // Much of this file was lifted from PhysicsFS, http://icculus.org/physfs/ ... // I wrote all this code under the same open source license, and own the // copyright on it anyhow, so transferring it here is "safe". // Forcibly disable UNICODE, since we manage this ourselves. #ifdef UNICODE #undef UNICODE #endif #define WIN32_LEAN_AND_MEAN 1 #include #include // !!! FIXME: this is for stdio redirection crap. #include #include // is Win95/Win98/WinME? (no Unicode, etc) static boolean osIsWin9x = false; static uint32 osMajorVer = 0; static uint32 osMinorVer = 0; static uint32 osBuildVer = 0; static uint32 startupTime = 0; // These allocation macros are much more complicated in PhysicsFS. #define smallAlloc(x) xmalloc(x) #define smallFree(x) free(x) // ...so is this. #define BAIL_IF_MACRO(cond, err, ret) if (cond) return ret; #define BAIL_MACRO(err, ret) return ret; #define LOWORDER_UINT64(pos) ((uint32) (pos & 0xFFFFFFFF)) #define HIGHORDER_UINT64(pos) ((uint32) ((pos >> 32) & 0xFFFFFFFF)) // Users without the platform SDK don't have this defined. The original docs // for SetFilePointer() just said to compare with 0xFFFFFFFF, so this should // work as desired. #ifndef INVALID_SET_FILE_POINTER #define INVALID_SET_FILE_POINTER 0xFFFFFFFF #endif // just in case... #ifndef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF #endif // Not defined before the Vista SDK. #ifndef IO_REPARSE_TAG_SYMLINK #define IO_REPARSE_TAG_SYMLINK 0xA000000C #endif // This is in shlobj.h, but we can't include it due to universal.h conflicts. #ifndef CSIDL_PERSONAL #define CSIDL_PERSONAL 0x0005 #endif // This is in shlobj.h, but we can't include it due to universal.h conflicts. #ifndef SHGFP_TYPE_CURRENT #define SHGFP_TYPE_CURRENT 0x0000 #endif // these utf-8 functions may move to mojosetup.c some day... void utf8ToUcs2(const char *src, uint16 *dst, uint64 len) { len -= sizeof (uint16); // save room for null char. while (len >= sizeof (uint16)) { uint32 cp = utf8codepoint(&src); if (cp == 0) break; else if (cp == UNICODE_BOGUS_CHAR_VALUE) cp = UNICODE_BOGUS_CHAR_CODEPOINT; // !!! FIXME: UTF-16 surrogates? if (cp > 0xFFFF) cp = UNICODE_BOGUS_CHAR_CODEPOINT; *(dst++) = cp; len -= sizeof (uint16); } // while *dst = 0; } // utf8ToUcs2 static void utf8fromcodepoint(uint32 cp, char **_dst, uint64 *_len) { char *dst = *_dst; uint64 len = *_len; if (len == 0) return; if (cp > 0x10FFFF) cp = UNICODE_BOGUS_CHAR_CODEPOINT; else if ((cp == 0xFFFE) || (cp == 0xFFFF)) // illegal values. cp = UNICODE_BOGUS_CHAR_CODEPOINT; else { // There are seven "UTF-16 surrogates" that are illegal in UTF-8. switch (cp) { case 0xD800: case 0xDB7F: case 0xDB80: case 0xDBFF: case 0xDC00: case 0xDF80: case 0xDFFF: cp = UNICODE_BOGUS_CHAR_CODEPOINT; } // switch } // else // Do the encoding... if (cp < 0x80) { *(dst++) = (char) cp; len--; } // if else if (cp < 0x800) { if (len < 2) len = 0; else { *(dst++) = (char) ((cp >> 6) | 128 | 64); *(dst++) = (char) (cp & 0x3F) | 128; len -= 2; } // else } // else if else if (cp < 0x10000) { if (len < 3) len = 0; else { *(dst++) = (char) ((cp >> 12) | 128 | 64 | 32); *(dst++) = (char) ((cp >> 6) & 0x3F) | 128; *(dst++) = (char) (cp & 0x3F) | 128; len -= 3; } // else } // else if else { if (len < 4) len = 0; else { *(dst++) = (char) ((cp >> 18) | 128 | 64 | 32 | 16); *(dst++) = (char) ((cp >> 12) & 0x3F) | 128; *(dst++) = (char) ((cp >> 6) & 0x3F) | 128; *(dst++) = (char) (cp & 0x3F) | 128; len -= 4; } // else if } // else *_dst = dst; *_len = len; } // utf8fromcodepoint #define UTF8FROMTYPE(typ, src, dst, len) \ len--; \ while (len) \ { \ const uint32 cp = (uint32) *(src++); \ if (cp == 0) break; \ utf8fromcodepoint(cp, &dst, &len); \ } \ *dst = '\0'; \ void utf8FromUcs2(const uint16 *src, char *dst, uint64 len) { UTF8FROMTYPE(uint64, src, dst, len); } // utf8FromUcs4 #define UTF8_TO_UNICODE_STACK_MACRO(w_assignto, str) { \ if (str == NULL) \ w_assignto = NULL; \ else { \ const uint32 len = (uint32) ((strlen(str) * 4) + 1); \ w_assignto = (WCHAR *) smallAlloc(len); \ if (w_assignto != NULL) \ utf8ToUcs2(str, (uint16 *) w_assignto, len); \ } \ } \ static uint32 wStrLen(const WCHAR *wstr) { uint32 len = 0; while (*(wstr++)) len++; return len; } // wStrLen static char *unicodeToUtf8Heap(const WCHAR *w_str) { char *retval = NULL; if (w_str != NULL) { void *ptr = NULL; const uint32 len = (wStrLen(w_str) * 4) + 1; retval = (char *) xmalloc(len); utf8FromUcs2((const uint16 *) w_str, retval, len); retval = xrealloc(retval, strlen(retval) + 1); // shrink. } // if return retval; } // unicodeToUtf8Heap static char *codepageToUtf8Heap(const char *cpstr) { char *retval = NULL; if (cpstr != NULL) { const int len = (int) (strlen(cpstr) + 1); WCHAR *wbuf = (WCHAR *) smallAlloc(len * sizeof (WCHAR)); BAIL_IF_MACRO(wbuf == NULL, ERR_OUT_OF_MEMORY, NULL); MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, cpstr, len, wbuf, len); retval = (char *) xmalloc(len * 4); utf8FromUcs2(wbuf, retval, len * 4); smallFree(wbuf); } // if return retval; } // codepageToUtf8Heap // pointers for APIs that may not exist on some Windows versions... static HANDLE libKernel32 = NULL; static HANDLE libUserEnv = NULL; static HANDLE libAdvApi32 = NULL; static HANDLE libShell32 = NULL; static DWORD (WINAPI *pGetModuleFileNameW)(HMODULE, LPWCH, DWORD); static BOOL (WINAPI *pGetUserProfileDirectoryW)(HANDLE, LPWSTR, LPDWORD); static BOOL (WINAPI *pGetUserNameW)(LPWSTR, LPDWORD); static DWORD (WINAPI *pGetFileAttributesW)(LPCWSTR); static HANDLE (WINAPI *pFindFirstFileW)(LPCWSTR, LPWIN32_FIND_DATAW); static BOOL (WINAPI *pFindNextFileW)(HANDLE, LPWIN32_FIND_DATAW); static DWORD (WINAPI *pGetCurrentDirectoryW)(DWORD, LPWSTR); static BOOL (WINAPI *pDeleteFileW)(LPCWSTR); static BOOL (WINAPI *pRemoveDirectoryW)(LPCWSTR); static BOOL (WINAPI *pCreateDirectoryW)(LPCWSTR, LPSECURITY_ATTRIBUTES); static BOOL (WINAPI *pGetFileAttributesExA) (LPCSTR, GET_FILEEX_INFO_LEVELS, LPVOID); static BOOL (WINAPI *pGetFileAttributesExW) (LPCWSTR, GET_FILEEX_INFO_LEVELS, LPVOID); static DWORD (WINAPI *pFormatMessageW) (DWORD, LPCVOID, DWORD, DWORD, LPWSTR, DWORD, va_list *); static HANDLE (WINAPI *pCreateFileW) (LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE); static HRESULT (WINAPI *pSHGetFolderPathA)(HWND, int, HANDLE, DWORD, LPTSTR); static BOOL (WINAPI *pMoveFileW)(LPCWSTR, LPCWSTR); static void (WINAPI *pOutputDebugStringW)(LPCWSTR); // Fallbacks for missing Unicode functions on Win95/98/ME. These are filled // into the function pointers if looking up the real Unicode entry points // in the system DLLs fails, so they're never used on WinNT/XP/Vista/etc. // They make an earnest effort to convert to/from UTF-8 and UCS-2 to // the user's current codepage. static BOOL WINAPI fallbackGetUserNameW(LPWSTR buf, LPDWORD len) { const DWORD cplen = *len; char *cpstr = smallAlloc(cplen); BOOL retval = GetUserNameA(cpstr, len); if (buf != NULL) MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, cpstr, cplen, buf, *len); smallFree(cpstr); return retval; } // fallbackGetUserNameW static DWORD WINAPI fallbackFormatMessageW(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLangId, LPWSTR lpBuf, DWORD nSize, va_list *Arguments) { char *cpbuf = (char *) smallAlloc(nSize); DWORD retval = FormatMessageA(dwFlags, lpSource, dwMessageId, dwLangId, cpbuf, nSize, Arguments); if (retval > 0) MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,cpbuf,retval,lpBuf,nSize); smallFree(cpbuf); return retval; } // fallbackFormatMessageW static DWORD WINAPI fallbackGetModuleFileNameW(HMODULE hMod, LPWCH lpBuf, DWORD nSize) { char *cpbuf = (char *) smallAlloc(nSize); DWORD retval = GetModuleFileNameA(hMod, cpbuf, nSize); if (retval > 0) MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,cpbuf,retval,lpBuf,nSize); smallFree(cpbuf); return retval; } // fallbackGetModuleFileNameW static DWORD WINAPI fallbackGetFileAttributesW(LPCWSTR fname) { DWORD retval = 0; const int buflen = (int) (wStrLen(fname) + 1); char *cpstr = (char *) smallAlloc(buflen); WideCharToMultiByte(CP_ACP, 0, fname, buflen, cpstr, buflen, NULL, NULL); retval = GetFileAttributesA(cpstr); smallFree(cpstr); return retval; } // fallbackGetFileAttributesW static DWORD WINAPI fallbackGetCurrentDirectoryW(DWORD buflen, LPWSTR buf) { DWORD retval = 0; char *cpbuf = NULL; if (buf != NULL) cpbuf = (char *) smallAlloc(buflen); retval = GetCurrentDirectoryA(buflen, cpbuf); if (cpbuf != NULL) { MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,cpbuf,retval,buf,buflen); smallFree(cpbuf); } // if return retval; } // fallbackGetCurrentDirectoryW static BOOL WINAPI fallbackRemoveDirectoryW(LPCWSTR dname) { BOOL retval = 0; const int buflen = (int) (wStrLen(dname) + 1); char *cpstr = (char *) smallAlloc(buflen); WideCharToMultiByte(CP_ACP, 0, dname, buflen, cpstr, buflen, NULL, NULL); retval = RemoveDirectoryA(cpstr); smallFree(cpstr); return retval; } // fallbackRemoveDirectoryW static BOOL WINAPI fallbackCreateDirectoryW(LPCWSTR dname, LPSECURITY_ATTRIBUTES attr) { BOOL retval = 0; const int buflen = (int) (wStrLen(dname) + 1); char *cpstr = (char *) smallAlloc(buflen); WideCharToMultiByte(CP_ACP, 0, dname, buflen, cpstr, buflen, NULL, NULL); retval = CreateDirectoryA(cpstr, attr); smallFree(cpstr); return retval; } // fallbackCreateDirectoryW static BOOL WINAPI fallbackDeleteFileW(LPCWSTR fname) { BOOL retval = 0; const int buflen = (int) (wStrLen(fname) + 1); char *cpstr = (char *) smallAlloc(buflen); WideCharToMultiByte(CP_ACP, 0, fname, buflen, cpstr, buflen, NULL, NULL); retval = DeleteFileA(cpstr); smallFree(cpstr); return retval; } // fallbackDeleteFileW static HANDLE WINAPI fallbackCreateFileW(LPCWSTR fname, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttrs, DWORD dwCreationDisposition, DWORD dwFlagsAndAttrs, HANDLE hTemplFile) { HANDLE retval; const int buflen = (int) (wStrLen(fname) + 1); char *cpstr = (char *) smallAlloc(buflen); WideCharToMultiByte(CP_ACP, 0, fname, buflen, cpstr, buflen, NULL, NULL); retval = CreateFileA(cpstr, dwDesiredAccess, dwShareMode, lpSecurityAttrs, dwCreationDisposition, dwFlagsAndAttrs, hTemplFile); smallFree(cpstr); return retval; } // fallbackCreateFileW static BOOL WINAPI fallbackMoveFileW(LPCWSTR src, LPCWSTR dst) { BOOL retval; const int srcbuflen = (int) (wStrLen(src) + 1); char *srccpstr = (char *) smallAlloc(srcbuflen); const int dstbuflen = (int) (wStrLen(dst) + 1); char *dstcpstr = (char *) smallAlloc(dstbuflen); WideCharToMultiByte(CP_ACP,0,src,srcbuflen,srccpstr,srcbuflen,NULL,NULL); WideCharToMultiByte(CP_ACP,0,dst,dstbuflen,dstcpstr,dstbuflen,NULL,NULL); retval = MoveFileA(srccpstr, dstcpstr); smallFree(srccpstr); smallFree(dstcpstr); return retval; } // fallbackMoveFileW static void WINAPI fallbackOutputDebugStringW(LPCWSTR str) { const int buflen = (int) (wStrLen(str) + 1); char *cpstr = (char *) smallAlloc(buflen); WideCharToMultiByte(CP_ACP, 0, str, buflen, cpstr, buflen, NULL, NULL); OutputDebugStringA(cpstr); smallFree(cpstr); } // fallbackOutputDebugStringW // A blatant abuse of pointer casting... static int symLookup(HMODULE dll, void **addr, const char *sym) { return ((*addr = GetProcAddress(dll, sym)) != NULL); } // symLookup static boolean findApiSymbols(void) { const boolean osHasUnicode = !osIsWin9x; HMODULE dll = NULL; #define LOOKUP_NOFALLBACK(x, reallyLook) { \ if (reallyLook) \ symLookup(dll, (void **) &p##x, #x); \ else \ p##x = NULL; \ } #define LOOKUP(x, reallyLook) { \ if ((!reallyLook) || (!symLookup(dll, (void **) &p##x, #x))) \ p##x = fallback##x; \ } // Apparently Win9x HAS the Unicode entry points, they just don't WORK. // ...so don't look them up unless we're on NT+. (see osHasUnicode.) dll = libUserEnv = LoadLibraryA("userenv.dll"); if (dll != NULL) LOOKUP_NOFALLBACK(GetUserProfileDirectoryW, osHasUnicode); // !!! FIXME: what do they call advapi32.dll on Win64? dll = libAdvApi32 = LoadLibraryA("advapi32.dll"); if (dll != NULL) LOOKUP(GetUserNameW, osHasUnicode); // !!! FIXME: what do they call kernel32.dll on Win64? dll = libKernel32 = LoadLibraryA("kernel32.dll"); if (dll != NULL) { LOOKUP_NOFALLBACK(GetFileAttributesExA, 1); LOOKUP_NOFALLBACK(GetFileAttributesExW, osHasUnicode); LOOKUP_NOFALLBACK(FindFirstFileW, osHasUnicode); LOOKUP_NOFALLBACK(FindNextFileW, osHasUnicode); LOOKUP(GetModuleFileNameW, osHasUnicode); LOOKUP(FormatMessageW, osHasUnicode); LOOKUP(GetFileAttributesW, osHasUnicode); LOOKUP(GetCurrentDirectoryW, osHasUnicode); LOOKUP(CreateDirectoryW, osHasUnicode); LOOKUP(RemoveDirectoryW, osHasUnicode); LOOKUP(CreateFileW, osHasUnicode); LOOKUP(DeleteFileW, osHasUnicode); LOOKUP(MoveFileW, osHasUnicode); LOOKUP(OutputDebugStringW, osHasUnicode); } // if // !!! FIXME: what do they call shell32.dll on Win64? dll = libShell32 = LoadLibraryA("shell32.dll"); if (dll != NULL) LOOKUP_NOFALLBACK(SHGetFolderPathA, 1); #undef LOOKUP_NOFALLBACK #undef LOOKUP return true; } // findApiSymbols // ok, now the actual platform layer implementation... boolean MojoPlatform_istty(void) { return (GetConsoleWindow() != 0); } // MojoPlatform_istty boolean MojoPlatform_launchBrowser(const char *url) { // msdn says: // "Returns a value greater than 32 if successful, or an error value that // is less than or equal to 32 otherwise." return (((int) ShellExecuteA(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL)) > 32); } // MojoPlatform_launchBrowser boolean MojoPlatform_installDesktopMenuItem(const char *data) { // !!! FIXME: write me. STUBBED("desktop menu support"); return false; } // MojoPlatform_installDesktopMenuItem boolean MojoPlatform_uninstallDesktopMenuItem(const char *data) { // !!! FIXME: write me. STUBBED("desktop menu support"); return false; } // MojoPlatform_uninstallDesktopMenuItem int MojoPlatform_exec(const char *cmd) { STUBBED("exec"); return 127; } // MojoPlatform_exec int MojoPlatform_runScript(const char *script, boolean devnull, const char **argv) { STUBBED("runScript"); return 0; } boolean MojoPlatform_spawnTerminal(void) { assert(!MojoPlatform_istty()); if (AllocConsole()) { int hCrt; FILE *hf; hCrt = _open_osfhandle((long) GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT); hf = _fdopen( hCrt, "w" ); *stdout = *hf; setvbuf( stdout, NULL, _IONBF, 0 ); hCrt = _open_osfhandle((long) GetStdHandle(STD_ERROR_HANDLE), _O_TEXT); hf = _fdopen( hCrt, "w" ); *stderr = *hf; setvbuf( stderr, NULL, _IONBF, 0 ); hCrt = _open_osfhandle((long) GetStdHandle(STD_INPUT_HANDLE), _O_TEXT); hf = _fdopen( hCrt, "r" ); *stdin = *hf; setvbuf( stdin, NULL, _IONBF, 0 ); return true; } // if return false; } // MojoPlatform_spawnTerminal uint8 *MojoPlatform_decodeImage(const uint8 *data, uint32 size, uint32 *w, uint32 *h) { return NULL; // !!! FIXME: try IPicture? } // MojoPlatform_decodeImage char *MojoPlatform_currentWorkingDir(void) { char *retval = NULL; WCHAR *wbuf = NULL; DWORD buflen = 0; buflen = pGetCurrentDirectoryW(buflen, NULL); wbuf = (WCHAR *) smallAlloc((buflen + 2) * sizeof (WCHAR)); BAIL_IF_MACRO(wbuf == NULL, ERR_OUT_OF_MEMORY, NULL); pGetCurrentDirectoryW(buflen, wbuf); if (wbuf[buflen - 2] == '\\') wbuf[buflen - 1] = '\0'; // just in case... else { wbuf[buflen - 1] = '\\'; wbuf[buflen] = '\0'; } // else retval = unicodeToUtf8Heap(wbuf); smallFree(wbuf); return retval; } // MojoPlatform_currentWorkingDir char *MojoPlatform_readlink(const char *linkname) { STUBBED("should use symlinks (reparse points) on Vista"); return NULL; } // MojoPlatform_readlink // !!! FIXME: this code sort of sucks. char *MojoPlatform_realpath(const char *path) { // !!! FIXME: this should return NULL if (path) doesn't exist? // !!! FIXME: Need to handle symlinks in Vista... // !!! FIXME: try GetFullPathName() instead? // this function should be UTF-8 clean. char *retval = NULL; char *p = NULL; if ((path == NULL) || (*path == '\0')) return NULL; retval = (char *) xmalloc(MAX_PATH); // If in \\server\path format, it's already an absolute path. // We'll need to check for "." and ".." dirs, though, just in case. if ((path[0] == '\\') && (path[1] == '\\')) strcpy(retval, path); else { char *currentDir = MojoPlatform_currentWorkingDir(); if (currentDir == NULL) { free(retval); return NULL; } // if if (path[1] == ':') // drive letter specified? { // Apparently, "D:mypath" is the same as "D:\\mypath" if // D: is not the current drive. However, if D: is the // current drive, then "D:mypath" is a relative path. Ugh. if (path[2] == '\\') // maybe an absolute path? strcpy(retval, path); else // definitely an absolute path. { if (path[0] == currentDir[0]) // current drive; relative. { strcpy(retval, currentDir); strcat(retval, path + 2); } // if else // not current drive; absolute. { retval[0] = path[0]; retval[1] = ':'; retval[2] = '\\'; strcpy(retval + 3, path + 2); } // else } // else } // if else // no drive letter specified. { if (path[0] == '\\') // absolute path. { retval[0] = currentDir[0]; retval[1] = ':'; strcpy(retval + 2, path); } // if else { strcpy(retval, currentDir); strcat(retval, path); } // else } // else free(currentDir); } // else // (whew.) Ok, now take out "." and ".." path entries... p = retval; while ( (p = strstr(p, "\\.")) != NULL) { // it's a "." entry that doesn't end the string. if (p[2] == '\\') memmove(p + 1, p + 3, strlen(p + 3) + 1); // it's a "." entry that ends the string. else if (p[2] == '\0') p[0] = '\0'; // it's a ".." entry. else if (p[2] == '.') { char *prevEntry = p - 1; while ((prevEntry != retval) && (*prevEntry != '\\')) prevEntry--; if (prevEntry == retval) // make it look like a "." entry. memmove(p + 1, p + 2, strlen(p + 2) + 1); else { if (p[3] != '\0') // doesn't end string. *prevEntry = '\0'; else // ends string. memmove(prevEntry + 1, p + 4, strlen(p + 4) + 1); p = prevEntry; } // else } // else if else { p++; // look past current char. } // else } // while // shrink the retval's memory block if possible... return (char *) xrealloc(retval, strlen(retval) + 1); } // MojoPlatform_realpath char *MojoPlatform_appBinaryPath(void) { DWORD buflen = 64; LPWSTR modpath = NULL; char *retval = NULL; DWORD rc = 0; while (true) { void *ptr = xrealloc(modpath, buflen * sizeof(WCHAR)); modpath = (LPWSTR) ptr; rc = pGetModuleFileNameW(NULL, modpath, buflen); if (rc == 0) { free(modpath); return NULL; } // if if (rc < buflen) { buflen = rc; break; } // if buflen *= 2; } // while if (buflen > 0) // just in case... { WCHAR *ptr = (modpath + buflen) - 1; while (ptr != modpath) { if (*ptr == '\\') break; ptr--; } // while if ((ptr != modpath) || (*ptr == '\\')) // should always be true... { *(ptr + 1) = '\0'; // chop off filename. retval = unicodeToUtf8Heap(modpath); } // else } // else free(modpath); return retval; } // MojoPlatform_appBinaryPath // Try to make use of GetUserProfileDirectoryW(), which isn't available on // some common variants of Win32. If we can't use this, we just punt and // use the binary path for the user dir, too. // // On success, module-scope variable (userDir) will have a pointer to // a malloc()'d string of the user's profile dir, and a non-zero value is // returned. If we can't determine the profile dir, (userDir) will // be NULL, and zero is returned. char *MojoPlatform_homedir(void) { char *userDir = NULL; // GetUserProfileDirectoryW() is only available on NT 4.0 and later. // This means Win95/98/ME (and CE?) users have to do without, so for // them, we'll default to the base directory when we can't get the // function pointer. Since this is originally an NT API, we don't // offer a non-Unicode fallback. if (pGetUserProfileDirectoryW != NULL) { HANDLE accessToken = NULL; // Security handle to process HANDLE processHandle = GetCurrentProcess(); if (OpenProcessToken(processHandle, TOKEN_QUERY, &accessToken)) { DWORD psize = 0; WCHAR dummy = 0; LPWSTR wstr = NULL; BOOL rc = 0; // Should fail. Will write the size of the profile path in // psize. Also note that the second parameter can't be // NULL or the function fails. rc = pGetUserProfileDirectoryW(accessToken, &dummy, &psize); if (rc == 0) // this should always be true! { // Allocate memory for the profile directory wstr = (LPWSTR) smallAlloc(psize * sizeof (WCHAR)); if (wstr != NULL) { if (pGetUserProfileDirectoryW(accessToken, wstr, &psize)) userDir = unicodeToUtf8Heap(wstr); smallFree(wstr); } // else } // if CloseHandle(accessToken); } // if } // if if (userDir == NULL) // couldn't get profile for some reason. { // Might just be a non-NT system; try SHGetFolderPathA()... if (pSHGetFolderPathA != NULL) // can be NULL if IE5+ isn't installed! { char shellPath[MAX_PATH]; HRESULT status = pSHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, shellPath); if (SUCCEEDED(status)) userDir = codepageToUtf8Heap(shellPath); } // if } // if if (userDir == NULL) // Still nothing?! { // this either means we had a catastrophic failure, or we're on a // Win95 system without at least Internet Explorer 5.0. Bleh! userDir = xstrdup("C:\\My Documents"); // good luck with that. } // if return userDir; } // MojoPlatform_homedir // This implementation is a bit naive. char *MojoPlatform_locale(void) { char lang[16]; char country[16]; const int langrc = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang, sizeof (lang)); const int ctryrc = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, country, sizeof (country)); // Win95 systems will fail, because they don't have LOCALE_SISO*NAME ... if ((langrc != 0) && (ctryrc != 0)) return format("%0_%1", lang, country); return NULL; } // MojoPlatform_locale char *MojoPlatform_osType(void) { if (osIsWin9x) return xstrdup("win9x"); else return xstrdup("winnt"); return NULL; } // MojoPlatform_ostype char *MojoPlatform_osVersion(void) { return format("%0.%1.%2", numstr(osMajorVer), numstr(osMinorVer), numstr(osBuildVer)); } // MojoPlatform_osversion char *MojoPlatform_osMachine(void) { // !!! FIXME: return "x86" for win32 and "x64" (bleh!) for win64. return NULL; } // MojoPlatform_osMachine void MojoPlatform_sleep(uint32 ticks) { Sleep(ticks); } // MojoPlatform_sleep uint32 MojoPlatform_ticks(void) { return GetTickCount() - startupTime; } // MojoPlatform_ticks void MojoPlatform_die(void) { STUBBED("Win32 equivalent of _exit()?"); _exit(86); } // MojoPlatform_die boolean MojoPlatform_unlink(const char *fname) { int retval = 0; LPWSTR wpath; UTF8_TO_UNICODE_STACK_MACRO(wpath, fname); if (pGetFileAttributesW(wpath) == FILE_ATTRIBUTE_DIRECTORY) retval = (pRemoveDirectoryW(wpath) != 0); else retval = (pDeleteFileW(wpath) != 0); smallFree(wpath); return retval; } // MojoPlatform_unlink boolean MojoPlatform_symlink(const char *src, const char *dst) { STUBBED("Vista has symlink support"); return false; } // MojoPlatform_symlink boolean MojoPlatform_mkdir(const char *path, uint16 perms) { // !!! FIXME: error if already exists? // !!! FIXME: perms? WCHAR *wpath; DWORD rc; UTF8_TO_UNICODE_STACK_MACRO(wpath, path); rc = pCreateDirectoryW(wpath, NULL); smallFree(wpath); return (rc != 0); } // MojoPlatform_mkdir boolean MojoPlatform_rename(const char *src, const char *dst) { WCHAR *srcwpath; WCHAR *dstwpath; BOOL rc; MojoPlatform_unlink(dst); // to match Unix rename()... UTF8_TO_UNICODE_STACK_MACRO(srcwpath, src); UTF8_TO_UNICODE_STACK_MACRO(dstwpath, dst); rc = pMoveFileW(srcwpath, dstwpath); smallFree(srcwpath); smallFree(dstwpath); return (rc != 0); } // MojoPlatform_rename boolean MojoPlatform_exists(const char *dir, const char *fname) { WCHAR *wpath; char *fullpath = NULL; boolean retval = false; if (fname == NULL) fullpath = xstrdup(dir); else { const size_t len = strlen(dir) + strlen(fname) + 1; fullpath = (char *) xmalloc(len); snprintf(fullpath, len, "%s\\%s", dir, fname); } // else UTF8_TO_UNICODE_STACK_MACRO(wpath, fullpath); retval = (pGetFileAttributesW(wpath) != INVALID_FILE_ATTRIBUTES); smallFree(wpath); free(fullpath); return retval; } // MojoPlatform_exists boolean MojoPlatform_writable(const char *fname) { boolean retval = false; DWORD attr = 0; WCHAR *wpath; UTF8_TO_UNICODE_STACK_MACRO(wpath, fname); attr = pGetFileAttributesW(wpath); smallFree(wpath); if (attr != INVALID_FILE_ATTRIBUTES) retval = ((attr & FILE_ATTRIBUTE_READONLY) == 0); return retval; } // MojoPlatform_writable boolean MojoPlatform_isdir(const char *dir) { boolean retval = false; LPWSTR wpath; UTF8_TO_UNICODE_STACK_MACRO(wpath, dir); retval = ((pGetFileAttributesW(wpath) & FILE_ATTRIBUTE_DIRECTORY) != 0); smallFree(wpath); return retval; } // MojoPlatform_isdir static boolean isSymlinkAttrs(const DWORD attr, const DWORD tag) { return ( (attr & FILE_ATTRIBUTE_REPARSE_POINT) && (tag == IO_REPARSE_TAG_SYMLINK) ); } // isSymlinkAttrs boolean MojoPlatform_issymlink(const char *fname) { // !!! FIXME: // Windows Vista can have NTFS symlinks. Can older Windows releases have // them when talking to a network file server? What happens when you // mount a NTFS partition on XP that was plugged into a Vista install // that made a symlink? boolean retval = false; LPWSTR wpath; HANDLE dir; WIN32_FIND_DATAW entw; // no unicode entry points? Probably no symlinks. if (pFindFirstFileW == NULL) return false; // !!! FIXME: filter wildcard chars? UTF8_TO_UNICODE_STACK_MACRO(wpath, fname); dir = pFindFirstFileW(wpath, &entw); smallFree(wpath); if (dir != INVALID_HANDLE_VALUE) { retval = isSymlinkAttrs(entw.dwFileAttributes, entw.dwReserved0); FindClose(dir); } // if return retval; } // MojoPlatform_issymlink boolean MojoPlatform_isfile(const char *dir) { return ((!MojoPlatform_isdir(dir)) && (!MojoPlatform_issymlink(dir))); } // MojoPlatform_isfile void *MojoPlatform_stdout(void) { return NULL; // unsupported on Windows. } // MojoPlatform_stdout void *MojoPlatform_open(const char *fname, uint32 flags, uint16 mode) { HANDLE *retval = NULL; DWORD accessMode = 0; DWORD createMode = 0; DWORD shareMode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE; WCHAR *wpath = NULL; HANDLE fd = 0; if (flags & MOJOFILE_READ) accessMode |= GENERIC_READ; if (flags & MOJOFILE_WRITE) accessMode |= GENERIC_WRITE; if (accessMode == 0) return NULL; // have to specify SOMETHING. // if (flags & MOJOFILE_APPEND) // unixflags |= O_APPEND; if ((flags & MOJOFILE_CREATE) && (flags & MOJOFILE_EXCLUSIVE)) createMode = CREATE_NEW; else if ((flags & MOJOFILE_CREATE) && (flags & MOJOFILE_TRUNCATE)) createMode = CREATE_ALWAYS; else if (flags & MOJOFILE_CREATE) createMode = OPEN_ALWAYS; else if (flags & MOJOFILE_TRUNCATE) createMode = TRUNCATE_EXISTING; else createMode = OPEN_EXISTING; // !!! FIXME STUBBED("file permissions"); UTF8_TO_UNICODE_STACK_MACRO(wpath, fname); fd = pCreateFileW(wpath, accessMode, shareMode, NULL, createMode, FILE_ATTRIBUTE_NORMAL, NULL); smallFree(wpath); if (fd != INVALID_HANDLE_VALUE) { retval = (HANDLE *) xmalloc(sizeof (HANDLE)); *retval = fd; } // if return retval; } // MojoPlatform_open int64 MojoPlatform_read(void *fd, void *buf, uint32 bytes) { HANDLE handle = *((HANDLE *) fd); DWORD br = 0; // Read data from the file // !!! FIXME: uint32 might be a greater # than DWORD if(!ReadFile(handle, buf, bytes, &br, NULL)) return -1; return (int64) br; } // MojoPlatform_read int64 MojoPlatform_write(void *fd, const void *buf, uint32 bytes) { HANDLE handle = *((HANDLE *) fd); DWORD bw = 0; // Read data from the file // !!! FIXME: uint32 might be a greater # than DWORD if(!WriteFile(handle, buf, bytes, &bw, NULL)) return -1; return (int64) bw; } // MojoPlatform_write int64 MojoPlatform_tell(void *fd) { return MojoPlatform_seek(fd, 0, MOJOSEEK_CURRENT); } // MojoPlatform_tell int64 MojoPlatform_seek(void *fd, int64 pos, MojoFileSeek whence) { HANDLE handle = *((HANDLE *) fd); DWORD highpos = HIGHORDER_UINT64(pos); const DWORD lowpos = LOWORDER_UINT64(pos); DWORD winwhence = 0; DWORD rc = 0; switch (whence) { case MOJOSEEK_SET: winwhence = FILE_BEGIN; break; case MOJOSEEK_CURRENT: winwhence = FILE_CURRENT; break; case MOJOSEEK_END: winwhence = FILE_END; break; default: return -1; // !!! FIXME: maybe just abort? } // switch rc = SetFilePointer(handle, lowpos, &highpos, winwhence); if ( (rc == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR) ) return -1; return (int64) ((((uint64) highpos) << 32) | ((uint64) rc)); } // MojoPlatform_seek int64 MojoPlatform_flen(void *fd) { HANDLE handle = *((HANDLE *) fd); int64 retval = 0; DWORD SizeHigh = 0; DWORD SizeLow = GetFileSize(handle, &SizeHigh); if ((SizeLow == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)) return -1; else { // Combine the high/low order to create the 64-bit position value retval = (int64) ((((uint64) SizeHigh) << 32) | ((uint64) SizeLow)); assert(retval >= 0); } // else return retval; } // MojoPlatform_flen boolean MojoPlatform_flush(void *fd) { HANDLE handle = *((HANDLE *) fd); return (FlushFileBuffers(handle) != 0); } // MojoPlatform_flush boolean MojoPlatform_close(void *fd) { HANDLE handle = *((HANDLE *) fd); boolean retval = (CloseHandle(handle) != 0); if (retval) free(fd); return retval; } // MojoPlatform_close typedef struct { HANDLE dir; boolean done; WIN32_FIND_DATA ent; WIN32_FIND_DATAW entw; } WinApiDir; void *MojoPlatform_opendir(const char *dirname) { const int unicode = (pFindFirstFileW != NULL) && (pFindNextFileW != NULL); size_t len = strlen(dirname); char *searchPath = NULL; WCHAR *wSearchPath = NULL; WinApiDir *retval = (WinApiDir *) xmalloc(sizeof (WinApiDir)); retval->dir = INVALID_HANDLE_VALUE; retval->done = false; // Allocate a new string for path, maybe '\\', "*", and NULL terminator searchPath = (char *) smallAlloc(len + 3); // Copy current dirname strcpy(searchPath, dirname); // if there's no '\\' at the end of the path, stick one in there. if (searchPath[len - 1] != '\\') { searchPath[len++] = '\\'; searchPath[len] = '\0'; } // if // Append the "*" to the end of the string strcat(searchPath, "*"); UTF8_TO_UNICODE_STACK_MACRO(wSearchPath, searchPath); if (unicode) retval->dir = pFindFirstFileW(wSearchPath, &retval->entw); else { const int len = (int) (wStrLen(wSearchPath) + 1); char *cp = (char *) smallAlloc(len); WideCharToMultiByte(CP_ACP, 0, wSearchPath, len, cp, len, 0, 0); retval->dir = FindFirstFileA(cp, &retval->ent); smallFree(cp); } // else smallFree(wSearchPath); smallFree(searchPath); if (retval->dir == INVALID_HANDLE_VALUE) { free(retval); return NULL; } // if return retval; } // MojoPlatform_opendir char *MojoPlatform_readdir(void *_dirhandle) { const int unicode = (pFindFirstFileW != NULL) && (pFindNextFileW != NULL); WinApiDir *dir = (WinApiDir *) _dirhandle; char *utf8 = NULL; if (dir->done) return NULL; if (unicode) { do { const DWORD attr = dir->entw.dwFileAttributes; const DWORD tag = dir->entw.dwReserved0; const WCHAR *fn = dir->entw.cFileName; if ((fn[0] == '.') && (fn[1] == '\0')) continue; if ((fn[0] == '.') && (fn[1] == '.') && (fn[2] == '\0')) continue; utf8 = unicodeToUtf8Heap(fn); dir->done = (pFindNextFileW(dir->dir, &dir->entw) == 0); return utf8; } while (pFindNextFileW(dir->dir, &dir->entw) != 0); } // if else // ANSI fallback. { do { const DWORD attr = dir->ent.dwFileAttributes; const DWORD tag = dir->ent.dwReserved0; const char *fn = dir->ent.cFileName; if ((fn[0] == '.') && (fn[1] == '\0')) continue; if ((fn[0] == '.') && (fn[1] == '.') && (fn[2] == '\0')) continue; utf8 = codepageToUtf8Heap(fn); dir->done = (FindNextFileA(dir->dir, &dir->ent) == 0); return utf8; } while (FindNextFileA(dir->dir, &dir->ent) != 0); } // else dir->done = true; return NULL; } // MojoPlatform_readdir void MojoPlatform_closedir(void *dirhandle) { WinApiDir *dir = (WinApiDir *) dirhandle; if (dir) { FindClose(dir->dir); free(dir); } // if } // MojoPlatform_closedir int64 MojoPlatform_filesize(const char *fname) { // !!! FIXME: this is lame. int64 retval = -1; void *fd = MojoPlatform_open(fname, MOJOFILE_READ, 0); STUBBED("use a stat()-like thing instead"); if (fd != NULL) { retval = MojoPlatform_seek(fd, 0, MOJOSEEK_END); MojoPlatform_close(fd); } // if return retval; } // MojoPlatform_filesize boolean MojoPlatform_perms(const char *fname, uint16 *p) { STUBBED("Windows permissions"); *p = 0; return true; } // MojoPlatform_perms uint16 MojoPlatform_defaultFilePerms(void) { STUBBED("Windows permissions"); return 0644; } // MojoPlatform_defaultFilePerms uint16 MojoPlatform_defaultDirPerms(void) { STUBBED("Windows permissions"); return 0755; } // MojoPlatform_defaultDirPerms uint16 MojoPlatform_makePermissions(const char *str, boolean *_valid) { STUBBED("Windows permissions"); *_valid = true; return 0; } // MojoPlatform_makePermissions boolean MojoPlatform_chmod(const char *fname, uint16 p) { STUBBED("Windows permissions"); return true; //return (chmod(fname, p) != -1); } // MojoPlatform_chmod static BOOL mediaInDrive(const char *drive) { // Prevent windows warning message appearing when checking media size UINT oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS); DWORD tmp = 0; // If the function succeeds, there's media in the drive BOOL retval = GetVolumeInformationA(drive,NULL,0,NULL,NULL,&tmp,NULL,0); // Revert back to old windows error handler SetErrorMode(oldErrorMode); return retval; } // mediaInDrive char *MojoPlatform_findMedia(const char *uniquefile) { // !!! FIXME: Probably shouldn't just check drive letters... char drive_str[4] = { 'x', ':', '\\', '\0' }; char ch; UINT rc; boolean found = false; for (ch = 'A'; ch <= 'Z'; ch++) { drive_str[0] = ch; rc = GetDriveTypeA(drive_str); if ((rc != DRIVE_UNKNOWN) && (rc != DRIVE_NO_ROOT_DIR)) { if (mediaInDrive(drive_str)) { drive_str[2] = '\0'; found = (MojoPlatform_exists(drive_str, uniquefile)); drive_str[2] = '\\'; if (found) return xstrdup(drive_str); } // if } // if } // for return NULL; } // MojoPlatform_findMedia void MojoPlatform_log(const char *str) { if (pOutputDebugStringW != NULL) // in case this gets called before init... { static const WCHAR endl[3] = { '\r', '\n', '\0' }; WCHAR *wstr; UTF8_TO_UNICODE_STACK_MACRO(wstr, str); STUBBED("OutputDebugString() is probably not best here"); pOutputDebugStringW(wstr); pOutputDebugStringW(endl); smallFree(wstr); } // if } // MojoPlatform_log typedef struct { HINSTANCE dll; HANDLE file; } WinApiDll; void *MojoPlatform_dlopen(const uint8 *img, size_t len) { WinApiDll *retval = NULL; char path[MAX_PATH]; char fname[MAX_PATH]; DWORD bw = 0; HANDLE handle; HINSTANCE dll; GetTempPath(sizeof (path), path); GetTempFileName(path, "mojosetup-plugin-", 0, fname); handle = CreateFileA(fname, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (handle == INVALID_HANDLE_VALUE) return NULL; WriteFile(handle, img, len, &bw, NULL); // dump it to the temp file. CloseHandle(handle); if (bw != len) { DeleteFile(fname); return NULL; } // if // The DELETE_ON_CLOSE will cause the kernel to remove the file when // we're done with it, including manually closing or the process // terminating (including crashing). We hold this handle until we close // the library. handle = CreateFileA(fname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL); if (handle == INVALID_HANDLE_VALUE) { DeleteFile(fname); return NULL; } // if dll = LoadLibraryA(fname); if (dll == NULL) { CloseHandle(handle); // (also deletes temp file.) return NULL; } // if retval = (WinApiDll *) xmalloc(sizeof (WinApiDll)); retval->dll = dll; retval->file = handle; return retval; } // MojoPlatform_dlopen void *MojoPlatform_dlsym(void *_lib, const char *sym) { const WinApiDll *lib = (const WinApiDll *) _lib; return ((lib) ? GetProcAddress(lib->dll, sym) : NULL); } // MojoPlatform_dlsym void MojoPlatform_dlclose(void *_lib) { WinApiDll *lib = (WinApiDll *) _lib; if (lib) { FreeLibrary(lib->dll); CloseHandle(lib->file); // this also deletes the temp file. free(lib); } // if } // MojoPlatform_dlclose uint64 MojoPlatform_getuid(void) { return 0; // !!! FIXME } // MojoPlatform_getuid uint64 MojoPlatform_geteuid(void) { return 0; // !!! FIXME } // MojoPlatform_geteuid uint64 MojoPlatform_getgid(void) { return 0; // !!! FIXME } // MojoPlatform_getgid // Get OS info and save the important parts. // Returns non-zero if successful, otherwise it returns zero on failure. static boolean getOSInfo(void) { OSVERSIONINFO osVerInfo; osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo); if (!GetVersionEx(&osVerInfo)) return false; osIsWin9x = (osVerInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); osMajorVer = (uint32) osVerInfo.dwMajorVersion; osMinorVer = (uint32) osVerInfo.dwMinorVersion; osBuildVer = (uint32) osVerInfo.dwBuildNumber; return true; } // getOSInfo static boolean platformInit(void) { startupTime = GetTickCount(); if (!getOSInfo()) return false; if (!findApiSymbols()) return false; return true; } // platformInit static void platformDeinit(void) { HANDLE *libs[] = { &libKernel32, &libUserEnv, &libAdvApi32, &libShell32 }; int i; for (i = 0; i < (sizeof (libs) / sizeof (libs[0])); i++) { const HANDLE lib = *(libs[i]); if (lib) { FreeLibrary(lib); *(libs[i]) = NULL; } // if } // for } // platformDeinit static void buildCommandlineArray(LPSTR szCmd, int *_argc, char ***_argv) { int argc = 0; char **argv = NULL; // !!! FIXME: STUBBED("parse command line string into array"); *_argc = argc; *_argv = argv; } // buildCommandlineArray static void freeCommandlineArray(int argc, char **argv) { while (argc--) free(argv[argc]); free(argv); } // freeCommandlineArray int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmd, int nCmdShow) { int retval = 0; int argc = 0; char **argv = NULL; if (!platformInit()) retval = 1; else { buildCommandlineArray(szCmd, &argc, &argv); //openlog("mojosetup", LOG_PID, LOG_USER); //atexit(closelog); STUBBED("signal handlers"); //install_signals(); retval = MojoSetup_main(argc, argv); freeCommandlineArray(argc, argv); platformDeinit(); } // else return retval; } // main #endif // PLATFORM_WINDOWS // end of windows.c ...