static int task_database_iterate( db_handle_t *_db, database_state_handle_t *db_state, database_info_handle_t *db) { const char *name = database_info_get_current_element_name(db); if (!name) return 0; if (database_info_get_type(db) == DATABASE_TYPE_ITERATE) if (path_contains_compressed_file(name)) database_info_set_type(db, DATABASE_TYPE_ITERATE_ARCHIVE); switch (database_info_get_type(db)) { case DATABASE_TYPE_ITERATE: return task_database_iterate_playlist(db_state, db, name); case DATABASE_TYPE_ITERATE_ARCHIVE: return task_database_iterate_playlist_archive(_db, db_state, db, name); case DATABASE_TYPE_ITERATE_LUTRO: return task_database_iterate_playlist_lutro(_db, db_state, db, name); case DATABASE_TYPE_SERIAL_LOOKUP: return task_database_iterate_serial_lookup(_db, db_state, db, name); case DATABASE_TYPE_CRC_LOOKUP: return task_database_iterate_crc_lookup(_db, db_state, db, name, NULL); case DATABASE_TYPE_NONE: default: break; } return 0; }
static bool content_file_init_extract( struct string_list *content, content_information_ctx_t *content_ctx, const struct retro_subsystem_info *special, union string_list_elem_attr *attr, char **error_string ) { unsigned i; for (i = 0; i < content->size; i++) { char temp_content[PATH_MAX_LENGTH]; char new_path[PATH_MAX_LENGTH]; bool block_extract = content->elems[i].attr.i & 1; const char *path = content->elems[i].data; const char *valid_ext = special ? special->roms[i].valid_extensions : content_ctx->valid_extensions; bool contains_compressed = path_contains_compressed_file(path); /* Block extract check. */ if (block_extract) continue; /* just use the first file in the archive */ if (!contains_compressed && !path_is_compressed_file(path)) continue; temp_content[0] = new_path[0] = '\0'; strlcpy(temp_content, path, sizeof(temp_content)); if (!valid_ext || !file_archive_extract_file( temp_content, sizeof(temp_content), valid_ext, !string_is_empty(content_ctx->directory_cache) ? content_ctx->directory_cache : NULL, new_path, sizeof(new_path))) { char str[1024]; snprintf(str, sizeof(str), "%s: %s.\n", msg_hash_to_str( MSG_FAILED_TO_EXTRACT_CONTENT_FROM_COMPRESSED_FILE), temp_content); return false; } string_list_set(content, i, new_path); if (!string_list_append(content_ctx->temporary_content, new_path, *attr)) return false; } return true; }
static int database_info_list_iterate_end_no_match( database_info_handle_t *db, database_state_handle_t *db_state, const char *path) { /* Reached end of database list, * CRC match probably didn't succeed. */ /* If this was a compressed file and no match in the database * list was found then expand the search list to include the * archive's contents. */ if (path_is_compressed_file(path) && !path_contains_compressed_file(path)) { struct string_list *archive_list = file_archive_get_file_list(path, NULL); if (archive_list && archive_list->size > 0) { unsigned i; for (i = 0; i < archive_list->size; i++) { char *new_path = (char*)malloc( PATH_MAX_LENGTH * sizeof(char)); size_t path_size = PATH_MAX_LENGTH * sizeof(char); size_t path_len = strlen(path); new_path[0] = '\0'; strlcpy(new_path, path, path_size); if (path_len + strlen(archive_list->elems[i].data) + 1 < PATH_MAX_LENGTH) { new_path[path_len] = '#'; strlcpy(new_path + path_len + 1, archive_list->elems[i].data, path_size - path_len); } string_list_append(db->list, new_path, archive_list->elems[i].attr); free(new_path); } string_list_free(archive_list); } } db_state->list_index = 0; db_state->entry_index = 0; if (db_state->crc != 0) db_state->crc = 0; return 0; }
/** * content_file_read: * @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 content_file_compressed_read * if path contains a compressed file, otherwise will call retro_read_file(). * * Returns: 1 if file read, 0 on error. */ static int content_file_read(const char *path, void **buf, ssize_t *length) { #ifdef HAVE_COMPRESSION if (path_contains_compressed_file(path)) { if (content_file_compressed_read(path, buf, NULL, length)) return 1; } #endif return retro_read_file(path, buf, length); }
/** * 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: 1 if file read, 0 on error. */ int read_file(const char *path, void **buf, ssize_t *length) { #ifdef HAVE_COMPRESSION if (path_contains_compressed_file(path)) { if (read_compressed_file(path, buf, NULL, length)) return 1; } #endif return read_generic_file(path, buf, length); }
/* Generic file loader. */ long read_file(const char *path, void **buf) { /* 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 * */ #ifdef HAVE_COMPRESSION if (path_contains_compressed_file(path)) return read_compressed_file(path,buf,0); #endif return read_generic_file(path,buf); }
static bool load_content_from_compressed_archive( struct string_list *temporary_content, struct retro_game_info *info, unsigned i, struct string_list* additional_path_allocs, bool need_fullpath, const char *path) { union string_list_elem_attr attributes; ssize_t new_path_len; char new_path[PATH_MAX_LENGTH]; char new_basedir[PATH_MAX_LENGTH]; bool ret = false; settings_t *settings = config_get_ptr(); rarch_system_info_t *sys_info= NULL; runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &sys_info); if (sys_info && sys_info->info.block_extract) return true; if (!need_fullpath || !path_contains_compressed_file(path)) return true; RARCH_LOG("Compressed file in case of need_fullpath." " Now extracting to temporary directory.\n"); strlcpy(new_basedir, settings->cache_directory, sizeof(new_basedir)); if (string_is_empty(new_basedir) || !path_is_directory(new_basedir)) { RARCH_WARN("Tried extracting to cache directory, but " "cache directory was not set or found. " "Setting cache directory to directory " "derived by basename...\n"); fill_pathname_basedir(new_basedir, path, sizeof(new_basedir)); } attributes.i = 0; fill_pathname_join(new_path, new_basedir, path_basename(path), sizeof(new_path)); ret = content_file_compressed_read(path, NULL, new_path, &new_path_len); if (!ret || new_path_len < 0) { RARCH_ERR("%s \"%s\".\n", msg_hash_to_str(MSG_COULD_NOT_READ_CONTENT_FILE), path); return false; } RARCH_LOG("New path is: [%s]\n", new_path); string_list_append(additional_path_allocs, new_path, attributes); info[i].path = additional_path_allocs->elems[additional_path_allocs->size -1 ].data; if (!string_list_append(temporary_content, new_path, attributes)) return false; return true; }
static int task_database_iterate_crc_lookup( db_handle_t *_db, database_state_handle_t *db_state, database_info_handle_t *db, const char *name, const char *archive_entry) { if (!db_state->list || (unsigned)db_state->list_index == (unsigned)db_state->list->size) return database_info_list_iterate_end_no_match(db, db_state, name); if (db_state->entry_index == 0) { char query[50]; query[0] = '\0'; /* don't scan files that can't be in this database */ if (!(path_contains_compressed_file(name) && core_info_database_match_archive_member( db_state->list->elems[db_state->list_index].data)) && !core_info_database_supports_content_path( db_state->list->elems[db_state->list_index].data, name)) return database_info_list_iterate_next(db_state); snprintf(query, sizeof(query), "{crc:or(b\"%08X\",b\"%08X\")}", db_state->crc, db_state->archive_crc); database_info_list_iterate_new(db_state, query); } if (db_state->info) { database_info_t *db_info_entry = &db_state->info->list[db_state->entry_index]; if (db_info_entry && db_info_entry->crc32) { #if 0 RARCH_LOG("CRC32: 0x%08X , entry CRC32: 0x%08X (%s).\n", db_state->crc, db_info_entry->crc32, db_info_entry->name); #endif if (db_state->archive_crc == db_info_entry->crc32) return database_info_list_iterate_found_match( _db, db_state, db, NULL); if (db_state->crc == db_info_entry->crc32) return database_info_list_iterate_found_match( _db, db_state, db, archive_entry); } } db_state->entry_index++; if (db_state->info) { if (db_state->entry_index >= db_state->info->count) return database_info_list_iterate_next(db_state); } /* If we haven't reached the end of the database list yet, * continue iterating. */ if (db_state->list_index < db_state->list->size) return 1; database_info_list_free(db_state->info); if (db_state->info) free(db_state->info); return 0; }
static bool load_content(const struct retro_subsystem_info *special, const struct string_list *content) { unsigned i; bool ret = true; struct string_list* additional_path_allocs = string_list_new(); struct retro_game_info *info = (struct retro_game_info*) calloc(content->size, sizeof(*info)); if (!info) return false; for (i = 0; i < content->size; i++) { const char *path = content->elems[i].data; int attr = content->elems[i].attr.i; bool need_fullpath = attr & 2; bool require_content = attr & 4; if (require_content && !*path) { RARCH_LOG("libretro core requires content, but nothing was provided.\n"); ret = false; goto end; } info[i].path = *path ? path : NULL; if (!need_fullpath && *path) { /* Load the content into memory. */ RARCH_LOG("Loading content file: %s.\n", path); /* First content file is significant, attempt to do patching, * CRC checking, etc. */ long size = i == 0 ? read_content_file(path, (void**)&info[i].data) : read_file(path, (void**)&info[i].data); if (size < 0) { RARCH_ERR("Could not read content file \"%s\".\n", path); ret = false; goto end; } info[i].size = size; } else { RARCH_LOG("Content loading skipped. Implementation will" " load it on its own.\n"); if (need_fullpath && path_contains_compressed_file(path)) { RARCH_LOG("Compressed file in case of need_fullpath." "Now extracting to temporary directory.\n"); if ((!strcmp(g_settings.extraction_directory,"")) || !path_is_directory(g_settings.extraction_directory)) { RARCH_ERR("Tried extracting to extraction directory, but " "extraction directory was not set or found. Exiting.\n"); rarch_assert(false); } char new_path[PATH_MAX]; union string_list_elem_attr attr; attr.i = 0; fill_pathname_join(new_path,g_settings.extraction_directory, path_basename(path),sizeof(new_path)); read_compressed_file(path,NULL,new_path); string_list_append(additional_path_allocs,new_path,attr); info[i].path = additional_path_allocs->elems [additional_path_allocs->size -1 ].data; } } } if (special) ret = pretro_load_game_special(special->id, info, content->size); else ret = pretro_load_game(*content->elems[0].data ? info : NULL); if (!ret) RARCH_ERR("Failed to load game.\n"); end: for (i = 0; i < content->size; i++) free((void*)info[i].data); string_list_free(additional_path_allocs); free(info); return ret; }
database_info_handle_t *database_info_dir_init(const char *dir, enum database_type type) { database_info_handle_t *db = (database_info_handle_t*) calloc(1, sizeof(*db)); unsigned i = 0; if (!db) return NULL; db->list = dir_list_new_special(dir, DIR_LIST_RECURSIVE, NULL); if (!db->list) goto error; db->list_ptr = 0; db->status = DATABASE_STATUS_ITERATE; db->type = type; if (db->list->size > 0) { for (i = 0; i < db->list->size; i++) { const char *path = db->list->elems[i].data; if (path_is_compressed_file(path) && !path_contains_compressed_file(path)) { struct string_list *archive_list = path_is_compressed_file(path) ? file_archive_get_file_list(path, NULL) : NULL; if (archive_list && archive_list->size > 0) { unsigned i; for (i = 0; i < archive_list->size; i++) { char new_path[PATH_MAX_LENGTH]; size_t path_len = strlen(path); new_path[0] = '\0'; strlcpy(new_path, path, sizeof(new_path)); if (path_len + strlen(archive_list->elems[i].data) + 1 < PATH_MAX_LENGTH) { new_path[path_len] = '#'; strlcpy(new_path + path_len + 1, archive_list->elems[i].data, sizeof(new_path) - path_len); } string_list_append(db->list, new_path, archive_list->elems[i].attr); } string_list_free(archive_list); } } } } return db; error: if (db) free(db); return NULL; }
static bool load_content_need_fullpath( struct retro_game_info *info, unsigned i, struct string_list* additional_path_allocs, bool need_fullpath, const char *path) { #ifdef HAVE_COMPRESSION ssize_t len; union string_list_elem_attr attributes; char new_path[PATH_MAX_LENGTH] = {0}; char new_basedir[PATH_MAX_LENGTH] = {0}; bool ret = false; settings_t *settings = config_get_ptr(); global_t *global = global_get_ptr(); rarch_system_info_t *sys_info= rarch_system_info_get_ptr(); if (sys_info && sys_info->info.block_extract) return true; if (!need_fullpath) return true; if (!path_contains_compressed_file(path)) return true; RARCH_LOG("Compressed file in case of need_fullpath." "Now extracting to temporary directory.\n"); strlcpy(new_basedir, settings->extraction_directory, sizeof(new_basedir)); if ((!strcmp(new_basedir, "")) || !path_is_directory(new_basedir)) { RARCH_WARN("Tried extracting to extraction directory, but " "extraction directory was not set or found. " "Setting extraction directory to directory " "derived by basename...\n"); fill_pathname_basedir(new_basedir, path, sizeof(new_basedir)); } attributes.i = 0; fill_pathname_join(new_path, new_basedir, path_basename(path), sizeof(new_path)); ret = read_compressed_file(path,NULL,new_path, &len); if (!ret || len < 0) { RARCH_ERR("%s \"%s\".\n", msg_hash_to_str(MSG_COULD_NOT_READ_CONTENT_FILE), path); return false; } string_list_append(additional_path_allocs,new_path, attributes); info[i].path = additional_path_allocs->elems [additional_path_allocs->size -1 ].data; /* global->temporary_content is initialized in init_content_file * The following part takes care of cleanup of the unzipped files * after exit. */ rarch_assert(global->temporary_content != NULL); string_list_append(global->temporary_content, new_path, attributes); #endif return true; }
/** * content_file_load: * @special : subsystem of content to be loaded. Can be NULL. * content : * * Load content file (for libretro core). * * Returns : true if successful, otherwise false. **/ static bool content_file_load( struct retro_game_info *info, const struct string_list *content, content_information_ctx_t *content_ctx, char **error_string, const struct retro_subsystem_info *special ) { unsigned i; retro_ctx_load_content_info_t load_info; char msg[1024]; struct string_list *additional_path_allocs = string_list_new(); msg[0] = '\0'; if (!additional_path_allocs) return false; for (i = 0; i < content->size; i++) { int attr = content->elems[i].attr.i; const char *path = content->elems[i].data; bool need_fullpath = attr & 2; bool require_content = attr & 4; if (require_content && string_is_empty(path)) { snprintf(msg, sizeof(msg), "%s\n", msg_hash_to_str(MSG_ERROR_LIBRETRO_CORE_REQUIRES_CONTENT)); *error_string = strdup(msg); goto error; } info[i].path = NULL; if (!string_is_empty(path)) info[i].path = path; if (!need_fullpath && !string_is_empty(path)) { /* Load the content into memory. */ ssize_t len = 0; if (!load_content_into_memory(i, path, (void**)&info[i].data, &len)) { snprintf(msg, sizeof(msg), "%s \"%s\".\n", msg_hash_to_str(MSG_COULD_NOT_READ_CONTENT_FILE), path); *error_string = strdup(msg); goto error; } info[i].size = len; } else { RARCH_LOG("%s\n", msg_hash_to_str( MSG_CONTENT_LOADING_SKIPPED_IMPLEMENTATION_WILL_DO_IT)); #ifdef HAVE_COMPRESSION if ( !content_ctx->block_extract && need_fullpath && path_contains_compressed_file(path) && !load_content_from_compressed_archive( content_ctx, &info[i], i, additional_path_allocs, need_fullpath, path, error_string)) goto error; #endif } } load_info.content = content; load_info.special = special; load_info.info = info; if (!core_load_game(&load_info)) { snprintf(msg, sizeof(msg), "%s.\n", msg_hash_to_str(MSG_FAILED_TO_LOAD_CONTENT)); *error_string = strdup(msg); goto error; } #ifdef HAVE_CHEEVOS if (!special) { const void *load_data = NULL; const char *path = content->elems[0].data; cheevos_set_cheats(); if (!string_is_empty(path)) load_data = info; cheevos_load(load_data); } #endif string_list_free(additional_path_allocs); return true; error: if (additional_path_allocs) string_list_free(additional_path_allocs); return false; }
/** * file_archive_get_file_crc32: * @path : filename path of archive * * Returns: CRC32 of the specified file in the archive, otherwise 0. * If no path within the archive is specified, the first * file found inside is used. **/ uint32_t file_archive_get_file_crc32(const char *path) { file_archive_transfer_t state; const struct file_archive_file_backend *backend = file_archive_get_file_backend(path); struct archive_extract_userdata userdata = {{0}}; bool returnerr = false; bool contains_compressed = false; const char *archive_path = NULL; if (!backend) return 0; contains_compressed = path_contains_compressed_file(path); if (contains_compressed) { archive_path = path_get_archive_delim(path); /* move pointer right after the delimiter to give us the path */ if (archive_path) archive_path += 1; } state.type = ARCHIVE_TRANSFER_INIT; state.archive_size = 0; state.handle = NULL; state.stream = NULL; state.footer = NULL; state.directory = NULL; state.data = NULL; state.backend = NULL; /* Initialize and open archive first. Sets next state type to ITERATE. */ file_archive_parse_file_iterate(&state, &returnerr, path, NULL, NULL, &userdata); for (;;) { /* Now find the first file in the archive. */ if (state.type == ARCHIVE_TRANSFER_ITERATE) file_archive_parse_file_iterate(&state, &returnerr, path, NULL, NULL, &userdata); /* If no path specified within archive, stop after * finding the first file. */ if (!contains_compressed) break; /* Stop when the right file in the archive is found. */ if (archive_path) { if (string_is_equal(userdata.extracted_file_path, archive_path)) break; } else break; } file_archive_parse_file_iterate_stop(&state); if (userdata.crc) return userdata.crc; return 0; }
static bool init_content_file_extract( struct string_list *temporary_content, struct string_list *content, const struct retro_subsystem_info *special, union string_list_elem_attr *attr ) { unsigned i; settings_t *settings = config_get_ptr(); rarch_system_info_t *system = NULL; runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &system); for (i = 0; i < content->size; i++) { char temp_content[PATH_MAX_LENGTH]; char new_path[PATH_MAX_LENGTH]; bool contains_compressed = NULL; bool block_extract = content->elems[i].attr.i & 1; const char *valid_ext = system->info.valid_extensions; /* Block extract check. */ if (block_extract) continue; contains_compressed = path_contains_compressed_file(content->elems[i].data); if (special) valid_ext = special->roms[i].valid_extensions; if (!contains_compressed) { /* just use the first file in the archive */ if (!path_is_compressed_file(content->elems[i].data)) continue; } temp_content[0] = new_path[0] = '\0'; strlcpy(temp_content, content->elems[i].data, sizeof(temp_content)); if (!valid_ext || !file_archive_extract_file(temp_content, sizeof(temp_content), valid_ext, *settings->directory.cache ? settings->directory.cache : NULL, new_path, sizeof(new_path))) { RARCH_ERR("%s: %s.\n", msg_hash_to_str( MSG_FAILED_TO_EXTRACT_CONTENT_FROM_COMPRESSED_FILE), temp_content); runloop_msg_queue_push( msg_hash_to_str(MSG_FAILED_TO_EXTRACT_CONTENT_FROM_COMPRESSED_FILE) , 2, 180, true); return false; } string_list_set(content, i, new_path); if (!string_list_append(temporary_content, new_path, *attr)) return false; } return true; }