| 1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
| 2 | * Mupen64plus-core - osal/files_win32.c * |
| 3 | * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * |
| 4 | * Copyright (C) 2009 Richard Goedeken * |
| 5 | * * |
| 6 | * This program is free software; you can redistribute it and/or modify * |
| 7 | * it under the terms of the GNU General Public License as published by * |
| 8 | * the Free Software Foundation; either version 2 of the License, or * |
| 9 | * (at your option) any later version. * |
| 10 | * * |
| 11 | * This program is distributed in the hope that it will be useful, * |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
| 14 | * GNU General Public License for more details. * |
| 15 | * * |
| 16 | * You should have received a copy of the GNU General Public License * |
| 17 | * along with this program; if not, write to the * |
| 18 | * Free Software Foundation, Inc., * |
| 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * |
| 20 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 21 | |
| 22 | /* This file contains the definitions for the unix-specific file handling |
| 23 | * functions |
| 24 | */ |
| 25 | |
| 26 | #include <sys/types.h> |
| 27 | #include <sys/stat.h> |
| 28 | #include <stdlib.h> |
| 29 | #include <string.h> |
| 30 | #include <stdio.h> |
| 31 | #include <direct.h> |
| 32 | #include <shlobj.h> |
| 33 | |
| 34 | #include "files.h" |
| 35 | #include "api/m64p_types.h" |
| 36 | #include "api/callbacks.h" |
| 37 | |
| 38 | /* definitions for system directories to search when looking for shared data files */ |
| 39 | #if defined(SHAREDIR) |
| 40 | #define XSTR(S) STR(S) /* this wacky preprocessor thing is necessary to generate a quote-enclosed */ |
| 41 | #define STR(S) #S /* copy of the SHAREDIR macro, which is defined by the makefile via gcc -DSHAREDIR="..." */ |
| 42 | static const int datasearchdirs = 2; |
| 43 | static const char *datasearchpath[2] = { XSTR(SHAREDIR), ".\\" }; |
| 44 | #undef STR |
| 45 | #undef XSTR |
| 46 | #else |
| 47 | static const int datasearchdirs = 1; |
| 48 | static const char *datasearchpath[1] = { ".\\" }; |
| 49 | #endif |
| 50 | |
| 51 | /* local functions */ |
| 52 | static int search_dir_file(char *destpath, const char *path, const char *filename) |
| 53 | { |
| 54 | struct _stat fileinfo; |
| 55 | |
| 56 | /* sanity check to start */ |
| 57 | if (destpath == NULL || path == NULL || filename == NULL) |
| 58 | return 1; |
| 59 | |
| 60 | /* build the full filepath */ |
| 61 | strcpy(destpath, path); |
| 62 | if (destpath[strlen(destpath)-1] != '\\') |
| 63 | strcat(destpath, "\\"); |
| 64 | strcat(destpath, filename); |
| 65 | |
| 66 | /* test for a valid file */ |
| 67 | if (_stat(destpath, &fileinfo) != 0) |
| 68 | return 2; |
| 69 | if ((fileinfo.st_mode & _S_IFREG) == 0) |
| 70 | return 3; |
| 71 | |
| 72 | /* success - file exists and is a regular file */ |
| 73 | return 0; |
| 74 | } |
| 75 | |
| 76 | /* global functions */ |
| 77 | |
| 78 | int osal_mkdirp(const char *dirpath, int mode) |
| 79 | { |
| 80 | char *mypath, *currpath, *lastchar; |
| 81 | struct _stat fileinfo; |
| 82 | |
| 83 | // Create a copy of the path, so we can modify it |
| 84 | mypath = currpath = _strdup(dirpath); |
| 85 | if (mypath == NULL) |
| 86 | return 1; |
| 87 | |
| 88 | // if the directory path ends with a separator, remove it |
| 89 | lastchar = mypath + strlen(mypath) - 1; |
| 90 | if (strchr(OSAL_DIR_SEPARATORS, *lastchar) != NULL) |
| 91 | *lastchar = 0; |
| 92 | |
| 93 | // Terminate quickly if the path already exists |
| 94 | if (_stat(mypath, &fileinfo) == 0 && (fileinfo.st_mode & _S_IFDIR)) |
| 95 | goto goodexit; |
| 96 | |
| 97 | while ((currpath = strpbrk(currpath + 1, OSAL_DIR_SEPARATORS)) != NULL) |
| 98 | { |
| 99 | // if slash is right after colon, then we are looking at drive name prefix (C:\) and should |
| 100 | // just skip it, because _stat and _mkdir will both fail for "C:" |
| 101 | if (currpath > mypath && currpath[-1] == ':') |
| 102 | continue; |
| 103 | *currpath = '\0'; |
| 104 | if (_stat(mypath, &fileinfo) != 0) |
| 105 | { |
| 106 | if (_mkdir(mypath) != 0) |
| 107 | goto errorexit; |
| 108 | } |
| 109 | else if (!(fileinfo.st_mode & _S_IFDIR)) |
| 110 | { |
| 111 | goto errorexit; |
| 112 | } |
| 113 | *currpath = OSAL_DIR_SEPARATORS[0]; |
| 114 | } |
| 115 | |
| 116 | // Create full path |
| 117 | if (_mkdir(mypath) != 0) |
| 118 | goto errorexit; |
| 119 | |
| 120 | goodexit: |
| 121 | free(mypath); |
| 122 | return 0; |
| 123 | errorexit: |
| 124 | free(mypath); |
| 125 | return 1; |
| 126 | } |
| 127 | |
| 128 | const char * osal_get_shared_filepath(const char *filename, const char *firstsearch, const char *secondsearch) |
| 129 | { |
| 130 | static char retpath[_MAX_PATH]; |
| 131 | int i; |
| 132 | |
| 133 | /* if caller gave us any directories to search, then look there first */ |
| 134 | if (firstsearch != NULL && search_dir_file(retpath, firstsearch, filename) == 0) |
| 135 | return retpath; |
| 136 | if (secondsearch != NULL && search_dir_file(retpath, secondsearch, filename) == 0) |
| 137 | return retpath; |
| 138 | |
| 139 | /* otherwise check our standard paths */ |
| 140 | if (search_dir_file(retpath, osal_get_user_configpath(), filename) == 0) |
| 141 | return retpath; |
| 142 | for (i = 0; i < datasearchdirs; i++) |
| 143 | { |
| 144 | if (search_dir_file(retpath, datasearchpath[i], filename) == 0) |
| 145 | return retpath; |
| 146 | } |
| 147 | |
| 148 | /* we couldn't find the file */ |
| 149 | return NULL; |
| 150 | } |
| 151 | |
| 152 | const char * osal_get_user_configpath(void) |
| 153 | { |
| 154 | static char chHomePath[MAX_PATH]; |
| 155 | LPITEMIDLIST pidl; |
| 156 | LPMALLOC pMalloc; |
| 157 | struct _stat fileinfo; |
| 158 | |
| 159 | // Get item ID list for the path of user's personal directory |
| 160 | HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, &pidl); |
| 161 | // get the path in a char string |
| 162 | SHGetPathFromIDList(pidl, chHomePath); |
| 163 | // do a bunch of crap just to free some memory |
| 164 | hr = SHGetMalloc(&pMalloc); |
| 165 | pMalloc->lpVtbl->Free(pMalloc, pidl); |
| 166 | pMalloc->lpVtbl->Release(pMalloc); |
| 167 | |
| 168 | // tack on 'mupen64plus' |
| 169 | if (chHomePath[strlen(chHomePath)-1] != '\\') |
| 170 | strcat(chHomePath, "\\"); |
| 171 | strcat(chHomePath, "Mupen64Plus"); |
| 172 | |
| 173 | // if this directory doesn't exist, then make it |
| 174 | if (_stat(chHomePath, &fileinfo) == 0) |
| 175 | { |
| 176 | strcat(chHomePath, "\\"); |
| 177 | return chHomePath; |
| 178 | } |
| 179 | else |
| 180 | { |
| 181 | osal_mkdirp(chHomePath, 0); |
| 182 | if (_stat(chHomePath, &fileinfo) == 0) |
| 183 | { |
| 184 | strcat(chHomePath, "\\"); |
| 185 | return chHomePath; |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | /* otherwise we are in trouble */ |
| 190 | DebugMessage(M64MSG_ERROR, "Failed to open configuration directory '%s'.", chHomePath); |
| 191 | return NULL; |
| 192 | } |
| 193 | |
| 194 | const char * osal_get_user_datapath(void) |
| 195 | { |
| 196 | // in windows, these are all the same |
| 197 | return osal_get_user_configpath(); |
| 198 | } |
| 199 | |
| 200 | const char * osal_get_user_cachepath(void) |
| 201 | { |
| 202 | // in windows, these are all the same |
| 203 | return osal_get_user_configpath(); |
| 204 | } |
| 205 | |
| 206 | |