diff options
Diffstat (limited to 'src/common/file_util.cpp')
| -rw-r--r-- | src/common/file_util.cpp | 910 |
1 files changed, 910 insertions, 0 deletions
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp new file mode 100644 index 000000000..8b47cb3e5 --- /dev/null +++ b/src/common/file_util.cpp | |||
| @@ -0,0 +1,910 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | #include "common.h" | ||
| 7 | #include "common_paths.h" | ||
| 8 | #include "file_util.h" | ||
| 9 | #include "string_util.h" | ||
| 10 | |||
| 11 | #ifdef _WIN32 | ||
| 12 | #include <windows.h> | ||
| 13 | #include <shlobj.h> // for SHGetFolderPath | ||
| 14 | #include <shellapi.h> | ||
| 15 | #include <commdlg.h> // for GetSaveFileName | ||
| 16 | #include <io.h> | ||
| 17 | #include <direct.h> // getcwd | ||
| 18 | #else | ||
| 19 | #include <sys/param.h> | ||
| 20 | #include <sys/types.h> | ||
| 21 | #include <dirent.h> | ||
| 22 | #include <errno.h> | ||
| 23 | #include <stdlib.h> | ||
| 24 | #endif | ||
| 25 | |||
| 26 | #if defined(__APPLE__) | ||
| 27 | #include <CoreFoundation/CFString.h> | ||
| 28 | #include <CoreFoundation/CFURL.h> | ||
| 29 | #include <CoreFoundation/CFBundle.h> | ||
| 30 | #endif | ||
| 31 | |||
| 32 | #include <algorithm> | ||
| 33 | #include <sys/stat.h> | ||
| 34 | |||
| 35 | #include "string_util.h" | ||
| 36 | |||
| 37 | #ifndef S_ISDIR | ||
| 38 | #define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) | ||
| 39 | #endif | ||
| 40 | |||
| 41 | #ifdef BSD4_4 | ||
| 42 | #define stat64 stat | ||
| 43 | #define fstat64 fstat | ||
| 44 | #endif | ||
| 45 | |||
| 46 | // This namespace has various generic functions related to files and paths. | ||
| 47 | // The code still needs a ton of cleanup. | ||
| 48 | // REMEMBER: strdup considered harmful! | ||
| 49 | namespace File | ||
| 50 | { | ||
| 51 | |||
| 52 | // Remove any ending forward slashes from directory paths | ||
| 53 | // Modifies argument. | ||
| 54 | static void StripTailDirSlashes(std::string &fname) | ||
| 55 | { | ||
| 56 | if (fname.length() > 1) | ||
| 57 | { | ||
| 58 | size_t i = fname.length() - 1; | ||
| 59 | while (fname[i] == DIR_SEP_CHR) | ||
| 60 | fname[i--] = '\0'; | ||
| 61 | } | ||
| 62 | return; | ||
| 63 | } | ||
| 64 | |||
| 65 | // Returns true if file filename exists | ||
| 66 | bool Exists(const std::string &filename) | ||
| 67 | { | ||
| 68 | struct stat64 file_info; | ||
| 69 | |||
| 70 | std::string copy(filename); | ||
| 71 | StripTailDirSlashes(copy); | ||
| 72 | |||
| 73 | #ifdef _WIN32 | ||
| 74 | int result = _tstat64(UTF8ToTStr(copy).c_str(), &file_info); | ||
| 75 | #else | ||
| 76 | int result = stat64(copy.c_str(), &file_info); | ||
| 77 | #endif | ||
| 78 | |||
| 79 | return (result == 0); | ||
| 80 | } | ||
| 81 | |||
| 82 | // Returns true if filename is a directory | ||
| 83 | bool IsDirectory(const std::string &filename) | ||
| 84 | { | ||
| 85 | struct stat64 file_info; | ||
| 86 | |||
| 87 | std::string copy(filename); | ||
| 88 | StripTailDirSlashes(copy); | ||
| 89 | |||
| 90 | #ifdef _WIN32 | ||
| 91 | int result = _tstat64(UTF8ToTStr(copy).c_str(), &file_info); | ||
| 92 | #else | ||
| 93 | int result = stat64(copy.c_str(), &file_info); | ||
| 94 | #endif | ||
| 95 | |||
| 96 | if (result < 0) { | ||
| 97 | WARN_LOG(COMMON, "IsDirectory: stat failed on %s: %s", | ||
| 98 | filename.c_str(), GetLastErrorMsg()); | ||
| 99 | return false; | ||
| 100 | } | ||
| 101 | |||
| 102 | return S_ISDIR(file_info.st_mode); | ||
| 103 | } | ||
| 104 | |||
| 105 | // Deletes a given filename, return true on success | ||
| 106 | // Doesn't supports deleting a directory | ||
| 107 | bool Delete(const std::string &filename) | ||
| 108 | { | ||
| 109 | INFO_LOG(COMMON, "Delete: file %s", filename.c_str()); | ||
| 110 | |||
| 111 | // Return true because we care about the file no | ||
| 112 | // being there, not the actual delete. | ||
| 113 | if (!Exists(filename)) | ||
| 114 | { | ||
| 115 | WARN_LOG(COMMON, "Delete: %s does not exist", filename.c_str()); | ||
| 116 | return true; | ||
| 117 | } | ||
| 118 | |||
| 119 | // We can't delete a directory | ||
| 120 | if (IsDirectory(filename)) | ||
| 121 | { | ||
| 122 | WARN_LOG(COMMON, "Delete failed: %s is a directory", filename.c_str()); | ||
| 123 | return false; | ||
| 124 | } | ||
| 125 | |||
| 126 | #ifdef _WIN32 | ||
| 127 | if (!DeleteFile(UTF8ToTStr(filename).c_str())) | ||
| 128 | { | ||
| 129 | WARN_LOG(COMMON, "Delete: DeleteFile failed on %s: %s", | ||
| 130 | filename.c_str(), GetLastErrorMsg()); | ||
| 131 | return false; | ||
| 132 | } | ||
| 133 | #else | ||
| 134 | if (unlink(filename.c_str()) == -1) { | ||
| 135 | WARN_LOG(COMMON, "Delete: unlink failed on %s: %s", | ||
| 136 | filename.c_str(), GetLastErrorMsg()); | ||
| 137 | return false; | ||
| 138 | } | ||
| 139 | #endif | ||
| 140 | |||
| 141 | return true; | ||
| 142 | } | ||
| 143 | |||
| 144 | // Returns true if successful, or path already exists. | ||
| 145 | bool CreateDir(const std::string &path) | ||
| 146 | { | ||
| 147 | INFO_LOG(COMMON, "CreateDir: directory %s", path.c_str()); | ||
| 148 | #ifdef _WIN32 | ||
| 149 | if (::CreateDirectory(UTF8ToTStr(path).c_str(), NULL)) | ||
| 150 | return true; | ||
| 151 | DWORD error = GetLastError(); | ||
| 152 | if (error == ERROR_ALREADY_EXISTS) | ||
| 153 | { | ||
| 154 | WARN_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: already exists", path.c_str()); | ||
| 155 | return true; | ||
| 156 | } | ||
| 157 | ERROR_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: %i", path.c_str(), error); | ||
| 158 | return false; | ||
| 159 | #else | ||
| 160 | if (mkdir(path.c_str(), 0755) == 0) | ||
| 161 | return true; | ||
| 162 | |||
| 163 | int err = errno; | ||
| 164 | |||
| 165 | if (err == EEXIST) | ||
| 166 | { | ||
| 167 | WARN_LOG(COMMON, "CreateDir: mkdir failed on %s: already exists", path.c_str()); | ||
| 168 | return true; | ||
| 169 | } | ||
| 170 | |||
| 171 | ERROR_LOG(COMMON, "CreateDir: mkdir failed on %s: %s", path.c_str(), strerror(err)); | ||
| 172 | return false; | ||
| 173 | #endif | ||
| 174 | } | ||
| 175 | |||
| 176 | // Creates the full path of fullPath returns true on success | ||
| 177 | bool CreateFullPath(const std::string &fullPath) | ||
| 178 | { | ||
| 179 | int panicCounter = 100; | ||
| 180 | INFO_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str()); | ||
| 181 | |||
| 182 | if (File::Exists(fullPath)) | ||
| 183 | { | ||
| 184 | INFO_LOG(COMMON, "CreateFullPath: path exists %s", fullPath.c_str()); | ||
| 185 | return true; | ||
| 186 | } | ||
| 187 | |||
| 188 | size_t position = 0; | ||
| 189 | while (true) | ||
| 190 | { | ||
| 191 | // Find next sub path | ||
| 192 | position = fullPath.find(DIR_SEP_CHR, position); | ||
| 193 | |||
| 194 | // we're done, yay! | ||
| 195 | if (position == fullPath.npos) | ||
| 196 | return true; | ||
| 197 | |||
| 198 | // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") | ||
| 199 | std::string const subPath(fullPath.substr(0, position + 1)); | ||
| 200 | if (!File::IsDirectory(subPath)) | ||
| 201 | File::CreateDir(subPath); | ||
| 202 | |||
| 203 | // A safety check | ||
| 204 | panicCounter--; | ||
| 205 | if (panicCounter <= 0) | ||
| 206 | { | ||
| 207 | ERROR_LOG(COMMON, "CreateFullPath: directory structure is too deep"); | ||
| 208 | return false; | ||
| 209 | } | ||
| 210 | position++; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | |||
| 215 | // Deletes a directory filename, returns true on success | ||
| 216 | bool DeleteDir(const std::string &filename) | ||
| 217 | { | ||
| 218 | INFO_LOG(COMMON, "DeleteDir: directory %s", filename.c_str()); | ||
| 219 | |||
| 220 | // check if a directory | ||
| 221 | if (!File::IsDirectory(filename)) | ||
| 222 | { | ||
| 223 | ERROR_LOG(COMMON, "DeleteDir: Not a directory %s", filename.c_str()); | ||
| 224 | return false; | ||
| 225 | } | ||
| 226 | |||
| 227 | #ifdef _WIN32 | ||
| 228 | if (::RemoveDirectory(UTF8ToTStr(filename).c_str())) | ||
| 229 | return true; | ||
| 230 | #else | ||
| 231 | if (rmdir(filename.c_str()) == 0) | ||
| 232 | return true; | ||
| 233 | #endif | ||
| 234 | ERROR_LOG(COMMON, "DeleteDir: %s: %s", filename.c_str(), GetLastErrorMsg()); | ||
| 235 | |||
| 236 | return false; | ||
| 237 | } | ||
| 238 | |||
| 239 | // renames file srcFilename to destFilename, returns true on success | ||
| 240 | bool Rename(const std::string &srcFilename, const std::string &destFilename) | ||
| 241 | { | ||
| 242 | INFO_LOG(COMMON, "Rename: %s --> %s", | ||
| 243 | srcFilename.c_str(), destFilename.c_str()); | ||
| 244 | if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) | ||
| 245 | return true; | ||
| 246 | ERROR_LOG(COMMON, "Rename: failed %s --> %s: %s", | ||
| 247 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||
| 248 | return false; | ||
| 249 | } | ||
| 250 | |||
| 251 | // copies file srcFilename to destFilename, returns true on success | ||
| 252 | bool Copy(const std::string &srcFilename, const std::string &destFilename) | ||
| 253 | { | ||
| 254 | INFO_LOG(COMMON, "Copy: %s --> %s", | ||
| 255 | srcFilename.c_str(), destFilename.c_str()); | ||
| 256 | #ifdef _WIN32 | ||
| 257 | if (CopyFile(UTF8ToTStr(srcFilename).c_str(), UTF8ToTStr(destFilename).c_str(), FALSE)) | ||
| 258 | return true; | ||
| 259 | |||
| 260 | ERROR_LOG(COMMON, "Copy: failed %s --> %s: %s", | ||
| 261 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||
| 262 | return false; | ||
| 263 | #else | ||
| 264 | |||
| 265 | // buffer size | ||
| 266 | #define BSIZE 1024 | ||
| 267 | |||
| 268 | char buffer[BSIZE]; | ||
| 269 | |||
| 270 | // Open input file | ||
| 271 | FILE *input = fopen(srcFilename.c_str(), "rb"); | ||
| 272 | if (!input) | ||
| 273 | { | ||
| 274 | ERROR_LOG(COMMON, "Copy: input failed %s --> %s: %s", | ||
| 275 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||
| 276 | return false; | ||
| 277 | } | ||
| 278 | |||
| 279 | // open output file | ||
| 280 | FILE *output = fopen(destFilename.c_str(), "wb"); | ||
| 281 | if (!output) | ||
| 282 | { | ||
| 283 | fclose(input); | ||
| 284 | ERROR_LOG(COMMON, "Copy: output failed %s --> %s: %s", | ||
| 285 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||
| 286 | return false; | ||
| 287 | } | ||
| 288 | |||
| 289 | // copy loop | ||
| 290 | while (!feof(input)) | ||
| 291 | { | ||
| 292 | // read input | ||
| 293 | int rnum = fread(buffer, sizeof(char), BSIZE, input); | ||
| 294 | if (rnum != BSIZE) | ||
| 295 | { | ||
| 296 | if (ferror(input) != 0) | ||
| 297 | { | ||
| 298 | ERROR_LOG(COMMON, | ||
| 299 | "Copy: failed reading from source, %s --> %s: %s", | ||
| 300 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||
| 301 | goto bail; | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | // write output | ||
| 306 | int wnum = fwrite(buffer, sizeof(char), rnum, output); | ||
| 307 | if (wnum != rnum) | ||
| 308 | { | ||
| 309 | ERROR_LOG(COMMON, | ||
| 310 | "Copy: failed writing to output, %s --> %s: %s", | ||
| 311 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||
| 312 | goto bail; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | // close files | ||
| 316 | fclose(input); | ||
| 317 | fclose(output); | ||
| 318 | return true; | ||
| 319 | bail: | ||
| 320 | if (input) | ||
| 321 | fclose(input); | ||
| 322 | if (output) | ||
| 323 | fclose(output); | ||
| 324 | return false; | ||
| 325 | #endif | ||
| 326 | } | ||
| 327 | |||
| 328 | // Returns the size of filename (64bit) | ||
| 329 | u64 GetSize(const std::string &filename) | ||
| 330 | { | ||
| 331 | if (!Exists(filename)) | ||
| 332 | { | ||
| 333 | WARN_LOG(COMMON, "GetSize: failed %s: No such file", filename.c_str()); | ||
| 334 | return 0; | ||
| 335 | } | ||
| 336 | |||
| 337 | if (IsDirectory(filename)) | ||
| 338 | { | ||
| 339 | WARN_LOG(COMMON, "GetSize: failed %s: is a directory", filename.c_str()); | ||
| 340 | return 0; | ||
| 341 | } | ||
| 342 | |||
| 343 | struct stat64 buf; | ||
| 344 | #ifdef _WIN32 | ||
| 345 | if (_tstat64(UTF8ToTStr(filename).c_str(), &buf) == 0) | ||
| 346 | #else | ||
| 347 | if (stat64(filename.c_str(), &buf) == 0) | ||
| 348 | #endif | ||
| 349 | { | ||
| 350 | DEBUG_LOG(COMMON, "GetSize: %s: %lld", | ||
| 351 | filename.c_str(), (long long)buf.st_size); | ||
| 352 | return buf.st_size; | ||
| 353 | } | ||
| 354 | |||
| 355 | ERROR_LOG(COMMON, "GetSize: Stat failed %s: %s", | ||
| 356 | filename.c_str(), GetLastErrorMsg()); | ||
| 357 | return 0; | ||
| 358 | } | ||
| 359 | |||
| 360 | // Overloaded GetSize, accepts file descriptor | ||
| 361 | u64 GetSize(const int fd) | ||
| 362 | { | ||
| 363 | struct stat64 buf; | ||
| 364 | if (fstat64(fd, &buf) != 0) { | ||
| 365 | ERROR_LOG(COMMON, "GetSize: stat failed %i: %s", | ||
| 366 | fd, GetLastErrorMsg()); | ||
| 367 | return 0; | ||
| 368 | } | ||
| 369 | return buf.st_size; | ||
| 370 | } | ||
| 371 | |||
| 372 | // Overloaded GetSize, accepts FILE* | ||
| 373 | u64 GetSize(FILE *f) | ||
| 374 | { | ||
| 375 | // can't use off_t here because it can be 32-bit | ||
| 376 | u64 pos = ftello(f); | ||
| 377 | if (fseeko(f, 0, SEEK_END) != 0) { | ||
| 378 | ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", | ||
| 379 | f, GetLastErrorMsg()); | ||
| 380 | return 0; | ||
| 381 | } | ||
| 382 | u64 size = ftello(f); | ||
| 383 | if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) { | ||
| 384 | ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", | ||
| 385 | f, GetLastErrorMsg()); | ||
| 386 | return 0; | ||
| 387 | } | ||
| 388 | return size; | ||
| 389 | } | ||
| 390 | |||
| 391 | // creates an empty file filename, returns true on success | ||
| 392 | bool CreateEmptyFile(const std::string &filename) | ||
| 393 | { | ||
| 394 | INFO_LOG(COMMON, "CreateEmptyFile: %s", filename.c_str()); | ||
| 395 | |||
| 396 | if (!File::IOFile(filename, "wb")) | ||
| 397 | { | ||
| 398 | ERROR_LOG(COMMON, "CreateEmptyFile: failed %s: %s", | ||
| 399 | filename.c_str(), GetLastErrorMsg()); | ||
| 400 | return false; | ||
| 401 | } | ||
| 402 | |||
| 403 | return true; | ||
| 404 | } | ||
| 405 | |||
| 406 | |||
| 407 | // Scans the directory tree gets, starting from _Directory and adds the | ||
| 408 | // results into parentEntry. Returns the number of files+directories found | ||
| 409 | u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry) | ||
| 410 | { | ||
| 411 | INFO_LOG(COMMON, "ScanDirectoryTree: directory %s", directory.c_str()); | ||
| 412 | // How many files + directories we found | ||
| 413 | u32 foundEntries = 0; | ||
| 414 | #ifdef _WIN32 | ||
| 415 | // Find the first file in the directory. | ||
| 416 | WIN32_FIND_DATA ffd; | ||
| 417 | |||
| 418 | HANDLE hFind = FindFirstFile(UTF8ToTStr(directory + "\\*").c_str(), &ffd); | ||
| 419 | if (hFind == INVALID_HANDLE_VALUE) | ||
| 420 | { | ||
| 421 | FindClose(hFind); | ||
| 422 | return foundEntries; | ||
| 423 | } | ||
| 424 | // windows loop | ||
| 425 | do | ||
| 426 | { | ||
| 427 | FSTEntry entry; | ||
| 428 | const std::string virtualName(TStrToUTF8(ffd.cFileName)); | ||
| 429 | #else | ||
| 430 | struct dirent dirent, *result = NULL; | ||
| 431 | |||
| 432 | DIR *dirp = opendir(directory.c_str()); | ||
| 433 | if (!dirp) | ||
| 434 | return 0; | ||
| 435 | |||
| 436 | // non windows loop | ||
| 437 | while (!readdir_r(dirp, &dirent, &result) && result) | ||
| 438 | { | ||
| 439 | FSTEntry entry; | ||
| 440 | const std::string virtualName(result->d_name); | ||
| 441 | #endif | ||
| 442 | // check for "." and ".." | ||
| 443 | if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || | ||
| 444 | ((virtualName[0] == '.') && (virtualName[1] == '.') && | ||
| 445 | (virtualName[2] == '\0'))) | ||
| 446 | continue; | ||
| 447 | entry.virtualName = virtualName; | ||
| 448 | entry.physicalName = directory; | ||
| 449 | entry.physicalName += DIR_SEP + entry.virtualName; | ||
| 450 | |||
| 451 | if (IsDirectory(entry.physicalName.c_str())) | ||
| 452 | { | ||
| 453 | entry.isDirectory = true; | ||
| 454 | // is a directory, lets go inside | ||
| 455 | entry.size = ScanDirectoryTree(entry.physicalName, entry); | ||
| 456 | foundEntries += (u32)entry.size; | ||
| 457 | } | ||
| 458 | else | ||
| 459 | { // is a file | ||
| 460 | entry.isDirectory = false; | ||
| 461 | entry.size = GetSize(entry.physicalName.c_str()); | ||
| 462 | } | ||
| 463 | ++foundEntries; | ||
| 464 | // Push into the tree | ||
| 465 | parentEntry.children.push_back(entry); | ||
| 466 | #ifdef _WIN32 | ||
| 467 | } while (FindNextFile(hFind, &ffd) != 0); | ||
| 468 | FindClose(hFind); | ||
| 469 | #else | ||
| 470 | } | ||
| 471 | closedir(dirp); | ||
| 472 | #endif | ||
| 473 | // Return number of entries found. | ||
| 474 | return foundEntries; | ||
| 475 | } | ||
| 476 | |||
| 477 | |||
| 478 | // Deletes the given directory and anything under it. Returns true on success. | ||
| 479 | bool DeleteDirRecursively(const std::string &directory) | ||
| 480 | { | ||
| 481 | INFO_LOG(COMMON, "DeleteDirRecursively: %s", directory.c_str()); | ||
| 482 | #ifdef _WIN32 | ||
| 483 | // Find the first file in the directory. | ||
| 484 | WIN32_FIND_DATA ffd; | ||
| 485 | HANDLE hFind = FindFirstFile(UTF8ToTStr(directory + "\\*").c_str(), &ffd); | ||
| 486 | |||
| 487 | if (hFind == INVALID_HANDLE_VALUE) | ||
| 488 | { | ||
| 489 | FindClose(hFind); | ||
| 490 | return false; | ||
| 491 | } | ||
| 492 | |||
| 493 | // windows loop | ||
| 494 | do | ||
| 495 | { | ||
| 496 | const std::string virtualName(TStrToUTF8(ffd.cFileName)); | ||
| 497 | #else | ||
| 498 | struct dirent dirent, *result = NULL; | ||
| 499 | DIR *dirp = opendir(directory.c_str()); | ||
| 500 | if (!dirp) | ||
| 501 | return false; | ||
| 502 | |||
| 503 | // non windows loop | ||
| 504 | while (!readdir_r(dirp, &dirent, &result) && result) | ||
| 505 | { | ||
| 506 | const std::string virtualName = result->d_name; | ||
| 507 | #endif | ||
| 508 | |||
| 509 | // check for "." and ".." | ||
| 510 | if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || | ||
| 511 | ((virtualName[0] == '.') && (virtualName[1] == '.') && | ||
| 512 | (virtualName[2] == '\0'))) | ||
| 513 | continue; | ||
| 514 | |||
| 515 | std::string newPath = directory + DIR_SEP_CHR + virtualName; | ||
| 516 | if (IsDirectory(newPath)) | ||
| 517 | { | ||
| 518 | if (!DeleteDirRecursively(newPath)) | ||
| 519 | { | ||
| 520 | #ifndef _WIN32 | ||
| 521 | closedir(dirp); | ||
| 522 | #endif | ||
| 523 | |||
| 524 | return false; | ||
| 525 | } | ||
| 526 | } | ||
| 527 | else | ||
| 528 | { | ||
| 529 | if (!File::Delete(newPath)) | ||
| 530 | { | ||
| 531 | #ifndef _WIN32 | ||
| 532 | closedir(dirp); | ||
| 533 | #endif | ||
| 534 | |||
| 535 | return false; | ||
| 536 | } | ||
| 537 | } | ||
| 538 | |||
| 539 | #ifdef _WIN32 | ||
| 540 | } while (FindNextFile(hFind, &ffd) != 0); | ||
| 541 | FindClose(hFind); | ||
| 542 | #else | ||
| 543 | } | ||
| 544 | closedir(dirp); | ||
| 545 | #endif | ||
| 546 | File::DeleteDir(directory); | ||
| 547 | |||
| 548 | return true; | ||
| 549 | } | ||
| 550 | |||
| 551 | // Create directory and copy contents (does not overwrite existing files) | ||
| 552 | void CopyDir(const std::string &source_path, const std::string &dest_path) | ||
| 553 | { | ||
| 554 | #ifndef _WIN32 | ||
| 555 | if (source_path == dest_path) return; | ||
| 556 | if (!File::Exists(source_path)) return; | ||
| 557 | if (!File::Exists(dest_path)) File::CreateFullPath(dest_path); | ||
| 558 | |||
| 559 | struct dirent dirent, *result = NULL; | ||
| 560 | DIR *dirp = opendir(source_path.c_str()); | ||
| 561 | if (!dirp) return; | ||
| 562 | |||
| 563 | while (!readdir_r(dirp, &dirent, &result) && result) | ||
| 564 | { | ||
| 565 | const std::string virtualName(result->d_name); | ||
| 566 | // check for "." and ".." | ||
| 567 | if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || | ||
| 568 | ((virtualName[0] == '.') && (virtualName[1] == '.') && | ||
| 569 | (virtualName[2] == '\0'))) | ||
| 570 | continue; | ||
| 571 | |||
| 572 | std::string source, dest; | ||
| 573 | source = source_path + virtualName; | ||
| 574 | dest = dest_path + virtualName; | ||
| 575 | if (IsDirectory(source)) | ||
| 576 | { | ||
| 577 | source += '/'; | ||
| 578 | dest += '/'; | ||
| 579 | if (!File::Exists(dest)) File::CreateFullPath(dest); | ||
| 580 | CopyDir(source, dest); | ||
| 581 | } | ||
| 582 | else if (!File::Exists(dest)) File::Copy(source, dest); | ||
| 583 | } | ||
| 584 | closedir(dirp); | ||
| 585 | #endif | ||
| 586 | } | ||
| 587 | |||
| 588 | // Returns the current directory | ||
| 589 | std::string GetCurrentDir() | ||
| 590 | { | ||
| 591 | char *dir; | ||
| 592 | // Get the current working directory (getcwd uses malloc) | ||
| 593 | if (!(dir = __getcwd(NULL, 0))) { | ||
| 594 | |||
| 595 | ERROR_LOG(COMMON, "GetCurrentDirectory failed: %s", | ||
| 596 | GetLastErrorMsg()); | ||
| 597 | return NULL; | ||
| 598 | } | ||
| 599 | std::string strDir = dir; | ||
| 600 | free(dir); | ||
| 601 | return strDir; | ||
| 602 | } | ||
| 603 | |||
| 604 | // Sets the current directory to the given directory | ||
| 605 | bool SetCurrentDir(const std::string &directory) | ||
| 606 | { | ||
| 607 | return __chdir(directory.c_str()) == 0; | ||
| 608 | } | ||
| 609 | |||
| 610 | #if defined(__APPLE__) | ||
| 611 | std::string GetBundleDirectory() | ||
| 612 | { | ||
| 613 | CFURLRef BundleRef; | ||
| 614 | char AppBundlePath[MAXPATHLEN]; | ||
| 615 | // Get the main bundle for the app | ||
| 616 | BundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle()); | ||
| 617 | CFStringRef BundlePath = CFURLCopyFileSystemPath(BundleRef, kCFURLPOSIXPathStyle); | ||
| 618 | CFStringGetFileSystemRepresentation(BundlePath, AppBundlePath, sizeof(AppBundlePath)); | ||
| 619 | CFRelease(BundleRef); | ||
| 620 | CFRelease(BundlePath); | ||
| 621 | |||
| 622 | return AppBundlePath; | ||
| 623 | } | ||
| 624 | #endif | ||
| 625 | |||
| 626 | #ifdef _WIN32 | ||
| 627 | std::string& GetExeDirectory() | ||
| 628 | { | ||
| 629 | static std::string DolphinPath; | ||
| 630 | if (DolphinPath.empty()) | ||
| 631 | { | ||
| 632 | TCHAR Dolphin_exe_Path[2048]; | ||
| 633 | GetModuleFileName(NULL, Dolphin_exe_Path, 2048); | ||
| 634 | DolphinPath = TStrToUTF8(Dolphin_exe_Path); | ||
| 635 | DolphinPath = DolphinPath.substr(0, DolphinPath.find_last_of('\\')); | ||
| 636 | } | ||
| 637 | return DolphinPath; | ||
| 638 | } | ||
| 639 | #endif | ||
| 640 | |||
| 641 | std::string GetSysDirectory() | ||
| 642 | { | ||
| 643 | std::string sysDir; | ||
| 644 | |||
| 645 | #if defined (__APPLE__) | ||
| 646 | sysDir = GetBundleDirectory(); | ||
| 647 | sysDir += DIR_SEP; | ||
| 648 | sysDir += SYSDATA_DIR; | ||
| 649 | #else | ||
| 650 | sysDir = SYSDATA_DIR; | ||
| 651 | #endif | ||
| 652 | sysDir += DIR_SEP; | ||
| 653 | |||
| 654 | INFO_LOG(COMMON, "GetSysDirectory: Setting to %s:", sysDir.c_str()); | ||
| 655 | return sysDir; | ||
| 656 | } | ||
| 657 | |||
| 658 | // Returns a string with a Dolphin data dir or file in the user's home | ||
| 659 | // directory. To be used in "multi-user" mode (that is, installed). | ||
| 660 | const std::string& GetUserPath(const unsigned int DirIDX, const std::string &newPath) | ||
| 661 | { | ||
| 662 | static std::string paths[NUM_PATH_INDICES]; | ||
| 663 | |||
| 664 | // Set up all paths and files on the first run | ||
| 665 | if (paths[D_USER_IDX].empty()) | ||
| 666 | { | ||
| 667 | #ifdef _WIN32 | ||
| 668 | paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; | ||
| 669 | #else | ||
| 670 | if (File::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) | ||
| 671 | paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; | ||
| 672 | else | ||
| 673 | paths[D_USER_IDX] = std::string(getenv("HOME") ? | ||
| 674 | getenv("HOME") : getenv("PWD") ? | ||
| 675 | getenv("PWD") : "") + DIR_SEP DOLPHIN_DATA_DIR DIR_SEP; | ||
| 676 | #endif | ||
| 677 | |||
| 678 | paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; | ||
| 679 | paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP; | ||
| 680 | paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; | ||
| 681 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | ||
| 682 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | ||
| 683 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | ||
| 684 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | ||
| 685 | paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP; | ||
| 686 | paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP; | ||
| 687 | paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP; | ||
| 688 | paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP; | ||
| 689 | paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP; | ||
| 690 | paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP; | ||
| 691 | paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG; | ||
| 692 | paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG; | ||
| 693 | paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; | ||
| 694 | } | ||
| 695 | |||
| 696 | if (!newPath.empty()) | ||
| 697 | { | ||
| 698 | if (!File::IsDirectory(newPath)) | ||
| 699 | { | ||
| 700 | WARN_LOG(COMMON, "Invalid path specified %s", newPath.c_str()); | ||
| 701 | return paths[DirIDX]; | ||
| 702 | } | ||
| 703 | else | ||
| 704 | { | ||
| 705 | paths[DirIDX] = newPath; | ||
| 706 | } | ||
| 707 | |||
| 708 | switch (DirIDX) | ||
| 709 | { | ||
| 710 | case D_ROOT_IDX: | ||
| 711 | paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP; | ||
| 712 | paths[D_SYSCONF_IDX] = paths[D_USER_IDX] + SYSCONF_DIR + DIR_SEP; | ||
| 713 | paths[F_SYSCONF_IDX] = paths[D_SYSCONF_IDX] + SYSCONF; | ||
| 714 | break; | ||
| 715 | |||
| 716 | case D_USER_IDX: | ||
| 717 | paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP; | ||
| 718 | paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; | ||
| 719 | paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP; | ||
| 720 | paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; | ||
| 721 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | ||
| 722 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | ||
| 723 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | ||
| 724 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | ||
| 725 | paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP; | ||
| 726 | paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP; | ||
| 727 | paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP; | ||
| 728 | paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP; | ||
| 729 | paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP; | ||
| 730 | paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP; | ||
| 731 | paths[D_SYSCONF_IDX] = paths[D_USER_IDX] + SYSCONF_DIR DIR_SEP; | ||
| 732 | paths[F_EMUCONFIG_IDX] = paths[D_CONFIG_IDX] + EMU_CONFIG; | ||
| 733 | paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG; | ||
| 734 | paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG; | ||
| 735 | paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; | ||
| 736 | break; | ||
| 737 | |||
| 738 | case D_CONFIG_IDX: | ||
| 739 | paths[F_EMUCONFIG_IDX] = paths[D_CONFIG_IDX] + EMU_CONFIG; | ||
| 740 | paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG; | ||
| 741 | paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG; | ||
| 742 | break; | ||
| 743 | |||
| 744 | case D_DUMP_IDX: | ||
| 745 | paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP; | ||
| 746 | paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP; | ||
| 747 | paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP; | ||
| 748 | break; | ||
| 749 | |||
| 750 | case D_LOGS_IDX: | ||
| 751 | paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; | ||
| 752 | } | ||
| 753 | } | ||
| 754 | |||
| 755 | return paths[DirIDX]; | ||
| 756 | } | ||
| 757 | |||
| 758 | //std::string GetThemeDir(const std::string& theme_name) | ||
| 759 | //{ | ||
| 760 | // std::string dir = File::GetUserPath(D_THEMES_IDX) + theme_name + "/"; | ||
| 761 | // | ||
| 762 | //#if !defined(_WIN32) | ||
| 763 | // // If theme does not exist in user's dir load from shared directory | ||
| 764 | // if (!File::Exists(dir)) | ||
| 765 | // dir = SHARED_USER_DIR THEMES_DIR "/" + theme_name + "/"; | ||
| 766 | //#endif | ||
| 767 | // | ||
| 768 | // return dir; | ||
| 769 | //} | ||
| 770 | |||
| 771 | bool WriteStringToFile(bool text_file, const std::string &str, const char *filename) | ||
| 772 | { | ||
| 773 | return File::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); | ||
| 774 | } | ||
| 775 | |||
| 776 | bool ReadFileToString(bool text_file, const char *filename, std::string &str) | ||
| 777 | { | ||
| 778 | File::IOFile file(filename, text_file ? "r" : "rb"); | ||
| 779 | auto const f = file.GetHandle(); | ||
| 780 | |||
| 781 | if (!f) | ||
| 782 | return false; | ||
| 783 | |||
| 784 | str.resize(static_cast<u32>(GetSize(f))); | ||
| 785 | return file.ReadArray(&str[0], str.size()); | ||
| 786 | } | ||
| 787 | |||
| 788 | IOFile::IOFile() | ||
| 789 | : m_file(NULL), m_good(true) | ||
| 790 | {} | ||
| 791 | |||
| 792 | IOFile::IOFile(std::FILE* file) | ||
| 793 | : m_file(file), m_good(true) | ||
| 794 | {} | ||
| 795 | |||
| 796 | IOFile::IOFile(const std::string& filename, const char openmode[]) | ||
| 797 | : m_file(NULL), m_good(true) | ||
| 798 | { | ||
| 799 | Open(filename, openmode); | ||
| 800 | } | ||
| 801 | |||
| 802 | IOFile::~IOFile() | ||
| 803 | { | ||
| 804 | Close(); | ||
| 805 | } | ||
| 806 | |||
| 807 | IOFile::IOFile(IOFile&& other) | ||
| 808 | : m_file(NULL), m_good(true) | ||
| 809 | { | ||
| 810 | Swap(other); | ||
| 811 | } | ||
| 812 | |||
| 813 | IOFile& IOFile::operator=(IOFile&& other) | ||
| 814 | { | ||
| 815 | Swap(other); | ||
| 816 | return *this; | ||
| 817 | } | ||
| 818 | |||
| 819 | void IOFile::Swap(IOFile& other) | ||
| 820 | { | ||
| 821 | std::swap(m_file, other.m_file); | ||
| 822 | std::swap(m_good, other.m_good); | ||
| 823 | } | ||
| 824 | |||
| 825 | bool IOFile::Open(const std::string& filename, const char openmode[]) | ||
| 826 | { | ||
| 827 | Close(); | ||
| 828 | #ifdef _WIN32 | ||
| 829 | _tfopen_s(&m_file, UTF8ToTStr(filename).c_str(), UTF8ToTStr(openmode).c_str()); | ||
| 830 | #else | ||
| 831 | m_file = fopen(filename.c_str(), openmode); | ||
| 832 | #endif | ||
| 833 | |||
| 834 | m_good = IsOpen(); | ||
| 835 | return m_good; | ||
| 836 | } | ||
| 837 | |||
| 838 | bool IOFile::Close() | ||
| 839 | { | ||
| 840 | if (!IsOpen() || 0 != std::fclose(m_file)) | ||
| 841 | m_good = false; | ||
| 842 | |||
| 843 | m_file = NULL; | ||
| 844 | return m_good; | ||
| 845 | } | ||
| 846 | |||
| 847 | std::FILE* IOFile::ReleaseHandle() | ||
| 848 | { | ||
| 849 | std::FILE* const ret = m_file; | ||
| 850 | m_file = NULL; | ||
| 851 | return ret; | ||
| 852 | } | ||
| 853 | |||
| 854 | void IOFile::SetHandle(std::FILE* file) | ||
| 855 | { | ||
| 856 | Close(); | ||
| 857 | Clear(); | ||
| 858 | m_file = file; | ||
| 859 | } | ||
| 860 | |||
| 861 | u64 IOFile::GetSize() | ||
| 862 | { | ||
| 863 | if (IsOpen()) | ||
| 864 | return File::GetSize(m_file); | ||
| 865 | else | ||
| 866 | return 0; | ||
| 867 | } | ||
| 868 | |||
| 869 | bool IOFile::Seek(s64 off, int origin) | ||
| 870 | { | ||
| 871 | if (!IsOpen() || 0 != fseeko(m_file, off, origin)) | ||
| 872 | m_good = false; | ||
| 873 | |||
| 874 | return m_good; | ||
| 875 | } | ||
| 876 | |||
| 877 | u64 IOFile::Tell() | ||
| 878 | { | ||
| 879 | if (IsOpen()) | ||
| 880 | return ftello(m_file); | ||
| 881 | else | ||
| 882 | return -1; | ||
| 883 | } | ||
| 884 | |||
| 885 | bool IOFile::Flush() | ||
| 886 | { | ||
| 887 | if (!IsOpen() || 0 != std::fflush(m_file)) | ||
| 888 | m_good = false; | ||
| 889 | |||
| 890 | return m_good; | ||
| 891 | } | ||
| 892 | |||
| 893 | bool IOFile::Resize(u64 size) | ||
| 894 | { | ||
| 895 | if (!IsOpen() || 0 != | ||
| 896 | #ifdef _WIN32 | ||
| 897 | // ector: _chsize sucks, not 64-bit safe | ||
| 898 | // F|RES: changed to _chsize_s. i think it is 64-bit safe | ||
| 899 | _chsize_s(_fileno(m_file), size) | ||
| 900 | #else | ||
| 901 | // TODO: handle 64bit and growing | ||
| 902 | ftruncate(fileno(m_file), size) | ||
| 903 | #endif | ||
| 904 | ) | ||
| 905 | m_good = false; | ||
| 906 | |||
| 907 | return m_good; | ||
| 908 | } | ||
| 909 | |||
| 910 | } // namespace | ||