afh: Unify name of init functions.
[paraslash.git] / aft.c
diff --git a/aft.c b/aft.c
index aebb3ef..0c4c932 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -320,8 +320,8 @@ enum afhi_offsets {
        CHUNKS_TOTAL_OFFSET = 20,
        /** The length of the audio file header (4 bytes). */
        HEADER_LEN_OFFSET = 24,
-       /** Was: The start of the audio file header (4 bytes). */
-       AFHI_UNUSED2_OFFSET = 28,
+       /** Size of the largest chunk in bytes. (4 bytes). */
+       AFHI_MAX_CHUNK_SIZE_OFFSET = 28,
        /** The seconds part of the chunk time (4 bytes). */
        CHUNK_TV_TV_SEC_OFFSET = 32,
        /** The microseconds part of the chunk time (4 bytes). */
@@ -361,7 +361,7 @@ static void save_afhi(struct afh_info *afhi, char *buf)
        write_u8(buf + AFHI_CHANNELS_OFFSET, afhi->channels);
        write_u32(buf + CHUNKS_TOTAL_OFFSET, afhi->chunks_total);
        write_u32(buf + HEADER_LEN_OFFSET, afhi->header_len);
-       write_u32(buf + AFHI_UNUSED2_OFFSET, 0);
+       write_u32(buf + AFHI_MAX_CHUNK_SIZE_OFFSET, afhi->max_chunk_size);
        write_u32(buf + CHUNK_TV_TV_SEC_OFFSET, afhi->chunk_tv.tv_sec);
        write_u32(buf + CHUNK_TV_TV_USEC_OFFSET, afhi->chunk_tv.tv_usec);
        p = buf + AFHI_INFO_STRING_OFFSET;
@@ -374,6 +374,7 @@ static void save_afhi(struct afh_info *afhi, char *buf)
        sprintf(p, "%s", afhi->tags.comment);
 }
 
+/* does not load the chunk table */
 static void load_afhi(const char *buf, struct afh_info *afhi)
 {
        afhi->seconds_total = read_u32(buf + AFHI_SECONDS_TOTAL_OFFSET);
@@ -383,6 +384,7 @@ static void load_afhi(const char *buf, struct afh_info *afhi)
        afhi->channels = read_u8(buf + AFHI_CHANNELS_OFFSET);
        afhi->chunks_total = read_u32(buf + CHUNKS_TOTAL_OFFSET);
        afhi->header_len = read_u32(buf + HEADER_LEN_OFFSET);
+       afhi->max_chunk_size = read_u32(buf + AFHI_MAX_CHUNK_SIZE_OFFSET);
        afhi->chunk_tv.tv_sec = read_u32(buf + CHUNK_TV_TV_SEC_OFFSET);
        afhi->chunk_tv.tv_usec = read_u32(buf + CHUNK_TV_TV_USEC_OFFSET);
        afhi->techinfo = (char *)buf + AFHI_INFO_STRING_OFFSET;
@@ -393,42 +395,37 @@ static void load_afhi(const char *buf, struct afh_info *afhi)
        afhi->tags.comment = afhi->tags.album + strlen(afhi->tags.album) + 1;
 }
 
+/* Only used for saving the chunk table, but not for loading. */
 static unsigned sizeof_chunk_table(struct afh_info *afhi)
 {
-       if (!afhi)
+       if (!afhi || !afhi->chunk_table)
                return 0;
        return 4 * (afhi->chunks_total + 1);
 }
 
-static uint32_t save_chunk_table(struct afh_info *afhi, char *buf)
+static void save_chunk_table(struct afh_info *afhi, char *buf)
 {
-       int i;
-       uint32_t max = 0, old = 0;
-
-       for (i = 0; i <= afhi->chunks_total; i++) {
-               uint32_t val = afhi->chunk_table[i];
-               write_u32(buf + 4 * i, val);
-               /*
-                * If the first chunk is the header, do not consider it for the
-                * calculation of the largest chunk size.
-                */
-               if (i == 0 || (i == 1 && afhi->header_len > 0)) {
-                       old = val;
-                       continue;
-               }
-               max = PARA_MAX(max, val - old);
-               old = val;
-       }
-       return max;
+       uint32_t n;
+
+       if (!afhi->chunk_table)
+               return;
+       for (n = 0; n <= afhi->chunks_total; n++)
+               write_u32(buf + 4 * n, afhi->chunk_table[n]);
 }
 
-static void load_chunk_table(struct afh_info *afhi, char *buf)
+static void load_chunk_table(struct afh_info *afhi, const struct osl_object *ct)
 {
        int i;
+       size_t sz;
 
-       afhi->chunk_table = para_malloc(sizeof_chunk_table(afhi));
-       for (i = 0; i <= afhi->chunks_total; i++)
-               afhi->chunk_table[i] = read_u32(buf + 4 * i);
+       if (!ct->data || ct->size < 4) {
+               afhi->chunk_table = NULL;
+               return;
+       }
+       sz  = PARA_MIN(((size_t)afhi->chunks_total + 1) * 4, ct->size) + 1;
+       afhi->chunk_table = para_malloc(sz);
+       for (i = 0; i <= afhi->chunks_total && i * 4 + 3 < ct->size; i++)
+               afhi->chunk_table[i] = read_u32(ct->data + 4 * i);
 }
 
 /**
@@ -594,6 +591,10 @@ static int get_hash_of_row(const struct osl_row *row, unsigned char **hash)
  *
  * \return The return value of the underlying call to osl_get_object().
  *
+ * After the call the members of the afhi structure point to mapped memory
+ * which is owned by the osl table, Hence the caller must not attempt to free
+ * this memory by calling \ref clear_afhi().
+ *
  * \sa get_chunk_table_of_row().
  */
 int get_afhi_of_row(const struct osl_row *row, struct afh_info *afhi)
@@ -623,7 +624,13 @@ static int save_afd(struct audio_file_data *afd)
                goto err;
        buf = shm_afd;
        buf += sizeof(*afd);
-       afd->max_chunk_size = save_chunk_table(&afd->afhi, buf);
+       save_chunk_table(&afd->afhi, buf);
+       if (afd->afhi.max_chunk_size == 0) { /* v0.5.x on-disk afhi */
+               set_max_chunk_size(&afd->afhi);
+               PARA_NOTICE_LOG("max chunk size unset, re-add required\n");
+       } else
+               PARA_INFO_LOG("using max chunk size from afhi\n");
+       afd->max_chunk_size = afd->afhi.max_chunk_size;
        *(struct audio_file_data *)shm_afd = *afd;
        shm_detach(shm_afd);
        return shmid;
@@ -648,14 +655,22 @@ int load_afd(int shmid, struct audio_file_data *afd)
 {
        void *shm_afd;
        int ret;
+       struct osl_object obj;
 
        ret = shm_attach(shmid, ATTACH_RO, &shm_afd);
        if (ret < 0)
                return ret;
+       ret = shm_size(shmid, &obj.size);
+       if (ret < 0)
+               goto detach;
        *afd = *(struct audio_file_data *)shm_afd;
-       load_chunk_table(&afd->afhi, shm_afd + sizeof(*afd));
+       obj.data = shm_afd + sizeof(*afd);
+       obj.size -= sizeof(*afd);
+       load_chunk_table(&afd->afhi, &obj);
+       ret = 1;
+detach:
        shm_detach(shm_afd);
-       return 1;
+       return ret;
 }
 
 static int get_local_time(uint64_t *seconds, char *buf, size_t size,
@@ -684,7 +699,7 @@ static int get_local_time(uint64_t *seconds, char *buf, size_t size,
        /*
         * If the given time is more than six month away from the current time,
         * we print only the year. The additional space character in the format
-        * string below makes the formated date align nicely with dates that
+        * string below makes the formatted date align nicely with dates that
         * contain the time (those written by the above strftime() statement).
         */
        if (!strftime(buf, size, "%b %e  %Y", tm))
@@ -805,7 +820,11 @@ static int print_chunk_table(struct ls_data *d, struct para_buffer *b)
                (long unsigned) d->afhi.chunk_tv.tv_usec
        );
        buf = chunk_table_obj.data;
-       for (i = 0; i <= d->afhi.chunks_total; i++)
+       for (
+               i = 0;
+               i <= d->afhi.chunks_total && 4 * i + 3 < chunk_table_obj.size;
+               i++
+       )
                para_printf(b, "%u ", (unsigned) read_u32(buf + 4 * i));
        para_printf(b, "\n");
        ret = 1;
@@ -891,7 +910,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
                        last_played_time,
                        bn? bn : "?");
        }
-       write_filename_items(b, d->path, lls_opt_given(r_b) > 0);
+       write_filename_items(b, d->path, lls_opt_given(r_b));
        if (lls_opt_given(r_a))
                WRITE_STATUS_ITEM(b, SI_SCORE, "%li\n", d->score);
        ret = write_attribute_items(b, att_buf, afsi);
@@ -915,6 +934,8 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
        WRITE_STATUS_ITEM(b, SI_CHUNK_TIME, "%lu\n", tv2ms(&afhi->chunk_tv));
        WRITE_STATUS_ITEM(b, SI_NUM_CHUNKS, "%" PRIu32 "\n",
                afhi->chunks_total);
+       WRITE_STATUS_ITEM(b, SI_MAX_CHUNK_SIZE, "%" PRIu32 "\n",
+               afhi->max_chunk_size);
        WRITE_STATUS_ITEM(b, SI_TECHINFO, "%s\n", afhi->techinfo);
        WRITE_STATUS_ITEM(b, SI_ARTIST, "%s\n", afhi->tags.artist);
        WRITE_STATUS_ITEM(b, SI_TITLE, "%s\n", afhi->tags.title);
@@ -961,8 +982,7 @@ out:
 static int make_status_items(void)
 {
        const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS);
-       char *argv[] = {"ls", "--path", "--admissible",
-               "--listing-mode=verbose"};
+       char *argv[] = {"ls", "--admissible", "--listing-mode=verbose"};
        struct ls_options opts = {.mode = LS_MODE_VERBOSE};
        struct para_buffer pb = {.max_size = shm_get_shmmax() - 1};
        time_t current_time;
@@ -1040,8 +1060,15 @@ again:
        d->afhi.chunk_table = afd->afhi.chunk_table = NULL;
        ret = osl(osl_open_disk_object(audio_file_table, current_aft_row,
                AFTCOL_CHUNKS, &chunk_table_obj));
-       if (ret < 0)
-               return ret;
+       if (ret < 0) {
+               if (!afh_supports_dynamic_chunks(d->afsi.audio_format_id))
+                       return ret;
+               PARA_INFO_LOG("no chunk table for %s\n", d->path);
+               chunk_table_obj.data = NULL;
+               chunk_table_obj.size = 0;
+       } else {
+               PARA_INFO_LOG("chunk table: %zu bytes\n", chunk_table_obj.size);
+       }
        ret = mmap_full_file(d->path, O_RDONLY, &map.data, &map.size, &afd->fd);
        if (ret < 0)
                goto out;
@@ -1058,7 +1085,7 @@ again:
        save_afsi(&new_afsi, &afsi_obj); /* in-place update */
 
        afd->audio_format_id = d->afsi.audio_format_id;
-       load_chunk_table(&afd->afhi, chunk_table_obj.data);
+       load_chunk_table(&afd->afhi, &chunk_table_obj);
        aced.aft_row = current_aft_row;
        aced.old_afsi = &d->afsi;
        /*
@@ -1071,7 +1098,8 @@ again:
        ret = save_afd(afd);
 out:
        free(afd->afhi.chunk_table);
-       osl_close_disk_object(&chunk_table_obj);
+       if (chunk_table_obj.data)
+               osl_close_disk_object(&chunk_table_obj);
        if (ret < 0) {
                PARA_ERROR_LOG("%s: %s\n", d->path, para_strerror(-ret));
                ret = score_delete(current_aft_row);
@@ -1211,7 +1239,7 @@ static int prepare_ls_row(struct osl_row *row, void *ls_opts)
 {
        int ret, i;
        struct ls_options *options = ls_opts;
-       bool basename_given = SERVER_CMD_OPT_GIVEN(LS, BASENAME, options->lpr) > 0;
+       bool basename_given = SERVER_CMD_OPT_GIVEN(LS, BASENAME, options->lpr);
        struct ls_data *d;
        struct ls_widths *w;
        unsigned short num_digits;
@@ -1673,6 +1701,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
                        &objs[AFTCOL_AFHI]));
                if (ret < 0)
                        goto out;
+               /* truncate the file to size zero if there is no chunk table */
                ret = osl(osl_update_object(audio_file_table, row, AFTCOL_CHUNKS,
                        &objs[AFTCOL_CHUNKS]));
                if (ret < 0)
@@ -2113,7 +2142,9 @@ static int copy_selector_info(__a_unused struct osl_table *table,
        ret = get_afsi_object_of_row(row, &target_afsi_obj);
        if (ret < 0)
                return ret;
-       load_afsi(&target_afsi, &target_afsi_obj);
+       ret = load_afsi(&target_afsi, &target_afsi_obj);
+       if (ret < 0)
+               return ret;
        old_afsi = target_afsi;
        if (cad->copy_all || y_given)
                target_afsi.lyrics_id = cad->source_afsi.lyrics_id;