+/**
+ * Mmap the given audio file and update statistics.
+ *
+ * \param aft_row Determines the audio file to be opened and updated.
+ * \param score The score of the audio file.
+ * \param afd Result pointer.
+ *
+ * On success, the numplayed field of the audio file selector info is increased
+ * and the lastplayed time is set to the current time. Finally, the score of
+ * the audio file is updated.
+ *
+ * \return Positive shmid on success, negative on errors.
+ */
+int open_and_update_audio_file(struct osl_row *aft_row, long score,
+ struct audio_file_data *afd)
+{
+ HASH_TYPE *aft_hash, file_hash[HASH_SIZE];
+ struct osl_object afsi_obj;
+ struct afs_info old_afsi, new_afsi;
+ int ret = get_hash_of_row(aft_row, &aft_hash);
+ struct afsi_change_event_data aced;
+ struct osl_object map, chunk_table_obj;
+ char *path;
+
+ if (ret < 0)
+ return ret;
+ ret = get_audio_file_path_of_row(aft_row, &path);
+ if (ret < 0)
+ return ret;
+ ret = get_afsi_object_of_row(aft_row, &afsi_obj);
+ if (ret < 0)
+ return ret;
+ ret = load_afsi(&old_afsi, &afsi_obj);
+ if (ret < 0)
+ return ret;
+ ret = get_afhi_of_row(aft_row, &afd->afhi);
+ if (ret < 0)
+ return ret;
+ afd->afhi.chunk_table = NULL;
+ ret = osl_open_disk_object(audio_file_table, aft_row,
+ AFTCOL_CHUNKS, &chunk_table_obj);
+ if (ret < 0)
+ goto err;
+ ret = mmap_full_file(path, O_RDONLY, &map.data,
+ &map.size, &afd->fd);
+ if (ret < 0)
+ goto err;
+ hash_function(map.data, map.size, file_hash);
+ ret = hash_compare(file_hash, aft_hash);
+ para_munmap(map.data, map.size);
+ if (ret) {
+ ret = -E_HASH_MISMATCH;
+ goto err;
+ }
+ new_afsi = old_afsi;
+ new_afsi.num_played++;
+ new_afsi.last_played = time(NULL);
+ save_afsi(&new_afsi, &afsi_obj); /* in-place update */
+
+ load_chunk_table(&afd->afhi, chunk_table_obj.data);
+ ret = make_status_items(afd, &old_afsi, path, score, file_hash);
+ if (ret < 0)
+ goto err;
+ aced.aft_row = aft_row;
+ aced.old_afsi = &old_afsi;
+ afs_event(AFSI_CHANGE, NULL, &aced);
+ ret = save_afd(afd);
+err:
+ free(afd->afhi.chunk_table);
+ free(afd->afhi.info_string);
+ osl_close_disk_object(&chunk_table_obj);
+ return ret;
+}