/* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2015 - Daniel De Matteis * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with RetroArch. * If not, see . */ #include "file_ops.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_COMPRESSION #include "file_extract.h" #endif #ifdef __HAIKU__ #include #endif #if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP) #include /* stat() is defined here */ #endif #if defined(__CELLOS_LV2__) #ifndef S_ISDIR #define S_ISDIR(x) (x & 0040000) #endif #endif #if defined(_WIN32) #ifdef _MSC_VER #define setmode _setmode #endif #ifdef _XBOX #include #define INVALID_FILE_ATTRIBUTES -1 #else #include #include #include #include #endif #else #include #include #include #include #endif /** * write_file: * @path : path to file. * @data : contents to write to the file. * @size : size of the contents. * * Writes data to a file. * * Returns: true (1) on success, false (0) otherwise. */ bool write_file(const char *path, const void *data, ssize_t size) { bool ret = false; FILE *file = fopen(path, "wb"); if (!file) return false; ret = fwrite(data, 1, size, file) == size; fclose(file); return ret; } /** * read_generic_file: * @path : path to file. * @buf : buffer to allocate and read the contents of the * file into. Needs to be freed manually. * * Read the contents of a file into @buf. * * Returns: number of items read, -1 on error. */ static bool read_generic_file(const char *path, void **buf, ssize_t *len) { long ret = 0, _len = 0; void *rom_buf = NULL; FILE *file = fopen(path, "rb"); if (!file) goto error; if (fseek(file, 0, SEEK_END) != 0) goto error; _len = ftell(file); if (_len < 0) goto error; if (fseek(file, 0, SEEK_SET) != 0) goto error; rom_buf = malloc(_len + 1); if (!rom_buf) goto error; if ((ret = fread(rom_buf, 1, _len, file)) < _len) RARCH_WARN("Didn't read whole file.\n"); if (!rom_buf) goto error; *buf = rom_buf; /* Allow for easy reading of strings to be safe. * Will only work with sane character formatting (Unix). */ ((char*)rom_buf)[_len] = '\0'; if (fclose(file) != 0) RARCH_WARN("Failed to close file stream.\n"); if (len) *len = ret; return true; error: if (file) fclose(file); if (rom_buf) free(rom_buf); *buf = NULL; return false; } #ifdef HAVE_COMPRESSION /* Generic compressed file loader. * Extracts to buf, unless optional_filename != 0 * Then extracts to optional_filename and leaves buf alone. */ bool read_compressed_file(const char * path, void **buf, const char* optional_filename, ssize_t *length) { const char* file_ext; char archive_path[PATH_MAX_LENGTH], *archive_found = NULL; if (optional_filename) { /* Safety check. * If optional_filename and optional_filename * exists, we simply return 0, * hoping that optional_filename is the * same as requested. */ if(path_file_exists(optional_filename)) { *length = 0; return true; } } //We split carchive path and relative path: strlcpy(archive_path,path,sizeof(archive_path)); archive_found = (char*)strchr(archive_path,'#'); rarch_assert(archive_found != NULL); //We assure that there is something after the '#' symbol if (strlen(archive_found) <= 1) { /* * This error condition happens for example, when * path = /path/to/file.7z, or * path = /path/to/file.7z# */ RARCH_ERR("Could not extract image path and carchive path from " "path: %s.\n", path); *length = 0; return false; } /* We split the string in two, by putting a \0, where the hash was: */ *archive_found = '\0'; archive_found += 1; file_ext = path_get_extension(archive_path); #ifdef HAVE_7ZIP if (strcasecmp(file_ext,"7z") == 0) { *length = read_7zip_file(archive_path,archive_found,buf,optional_filename); if (*length != -1) return true; } #endif #ifdef HAVE_ZLIB if (strcasecmp(file_ext,"zip") == 0) { *length = read_zip_file(archive_path,archive_found,buf,optional_filename); if (*length != -1) return true; } #endif return false; } #endif /** * read_file: * @path : path to file. * @buf : buffer to allocate and read the contents of the * file into. Needs to be freed manually. * @length : Number of items read, -1 on error. * * Read the contents of a file into @buf. Will call read_compressed_file * if path contains a compressed file, otherwise will call read_generic_file. * * Returns: true if file read, false on error. */ int read_file(const char *path, void **buf, ssize_t *length) { #ifdef HAVE_COMPRESSION /* Here we check, whether the file, we are about to read is * inside an archive, or not. * * We determine, whether a file is inside a compressed archive, * by checking for the # inside the URL. * * For example: fullpath: /home/user/game.7z/mygame.rom * carchive_path: /home/user/game.7z * */ if (path_contains_compressed_file(path)) if (read_compressed_file(path, buf, NULL, length)) return true; #endif if (read_generic_file(path, buf, length)) return 1; *length = -1; return 0; }