예제 #1
0
off_t oggedit_write_flac_metadata(DB_FILE *in, const char *fname, const off_t offset, const int num_tags, char **tags)
{
    off_t res;
    char tempname[PATH_MAX] = "";
    ogg_packet **headers = NULL;
    char *vendor = NULL;
    ogg_sync_state oy;
    ogg_sync_init(&oy);

    /* Original file must be writable whichever way we update it */
    FILE *out = fopen(fname, "r+b");
    if (!out) {
        res = OGGEDIT_CANNOT_UPDATE_FILE;
        goto cleanup;
    }

    /* See if we can write the tags packet directly into the existing file ... */
    if (!(headers = metadata_block_packets(in, &oy, offset, &vendor, (int *)&res)))
        goto cleanup;
    const off_t stream_size_k = in->vfs->getlength(in) / 1000; // use file size for now
    const size_t metadata_size = 4 + vc_size(vendor, num_tags, tags);
    ptrdiff_t padding = headers[0]->bytes - metadata_size;
    if (stream_size_k < 1000 || padding < 0 || (headers[1] && padding > 0) || padding > stream_size_k+metadata_size) {
        res = open_temp_file(fname, tempname, &out);
        if (res) {
            goto cleanup;
        }
    }

    /* Re-pad if writing the whole file */
    if (*tempname) {
        size_t i = 0, j = 0;
        while (headers[i]) {
            headers[j++] = headers[i];
            while (headers[++i] && (headers[i]->packet[0] & 0x3F) == PADTYPE) {
                ogg_packet_clear(headers[i]);
                free(headers[i]);
            }
        }
        headers[j] = NULL;
        padding = headers[1] || stream_size_k < 900 ? 0 : stream_size_k < 10000 ? 1024 : stream_size_k < 100000 ? 8192 : 65536;
    }

    /* Write pages until we reach the correct comment header */
    ogg_page og;
    const int flac_serial = copy_up_to_codec(in, out, &oy, &og, *tempname ? 0 : offset, offset, FLACNAME);
    if (flac_serial <= OGGEDIT_EOF) {
        res = flac_serial;
        goto cleanup;
    }
    if ((res = copy_up_to_header(in, out, &oy, &og, flac_serial)) <= OGGEDIT_EOF)
        goto cleanup;
    const long pageno = write_metadata_block_packets(out, flac_serial, vendor, num_tags, tags, padding, headers);
    if (pageno < OGGEDIT_EOF) {
        res = pageno;
        goto cleanup;
    }

    /* If we have tempfile, copy the remaining pages */
    if (*tempname) {
        if ((res = copy_remaining_pages(in, out, &oy, flac_serial, pageno)) <= OGGEDIT_EOF)
            goto cleanup;
        if (rename(tempname, fname)) {
            res = OGGEDIT_RENAME_FAILED;
            goto cleanup;
        }
    }

    res = file_size(fname);

cleanup:
    clear_header_list(headers);
    cleanup(in, out, &oy, vendor);
    if (res < OGGEDIT_OK)
        unlink(tempname);
    return res;
}
예제 #2
0
off_t oggedit_write_vorbis_metadata(DB_FILE *in, const char *fname, const off_t offset, const size_t stream_size, const int num_tags, char **tags)
{
    off_t res;
    char tempname[PATH_MAX] = "";
    char *vendor = NULL;
    ogg_packet codebooks;
    memset(&codebooks, '\0', sizeof(codebooks));
    ogg_sync_state oy;
    ogg_sync_init(&oy);

    /* Original file must be writable whichever way we update it */
    FILE *out = fopen(fname, "r+b");
    if (!out) {
        res = OGGEDIT_CANNOT_UPDATE_FILE;
        goto cleanup;
    }

    /* See if we can write the tags packet directly into the existing file ... */
    const ptrdiff_t tags_packet_size = check_vorbis_headers(in, &oy, offset, &vendor, &codebooks);
    if (tags_packet_size <= OGGEDIT_EOF) {
        res = tags_packet_size;
        goto cleanup;
    }
    const size_t metadata_size = strlen(VCMAGIC) + vc_size(vendor, num_tags, tags) + 1;
    ptrdiff_t padding = tags_packet_size - metadata_size;
    const off_t file_size_k = in->vfs->getlength(in) / 1000;
    const size_t stream_size_k = stream_size ? stream_size / 1000 : file_size_k;
    if (file_size_k < 100 || padding < 0 || padding > file_size_k/10+stream_size_k+metadata_size) {
        res = open_temp_file(fname, tempname, &out);
        if (res) {
            goto cleanup;
        }
    }

    /* Re-pad if writing the whole file */
    if (*tempname)
        padding = stream_size_k < 90 ? 0 : stream_size_k < 1000 ? 128 : stream_size_k < 10000 ? 1024 : 8192;

    /* Write pages until the correct comment header */
    ogg_page og;
    int64_t vorbis_serial = copy_up_to_codec(in, out, &oy, &og, *tempname ? 0 : offset, offset, VORBISNAME);
    if (vorbis_serial <= OGGEDIT_EOF) {
        res = vorbis_serial;
        goto cleanup;
    }
    vorbis_serial = copy_up_to_header(in, out, &oy, &og, vorbis_serial);
    if (vorbis_serial <= OGGEDIT_EOF) {
        res = vorbis_serial;
        goto cleanup;
    }
    const long pageno = write_vorbis_tags(out, vorbis_serial, vendor, num_tags, tags, padding, &codebooks);
    if (pageno < OGGEDIT_EOF) {
        res = pageno;
        goto cleanup;
    }

    /* If we have tempfile, copy the remaining pages */
    if (*tempname) {
        vorbis_serial = copy_remaining_pages(in, out, &oy, vorbis_serial, pageno);
        if (vorbis_serial <= OGGEDIT_EOF) {
            res = vorbis_serial;
            goto cleanup;
        }
        if (rename(tempname, fname)) {
            res = OGGEDIT_RENAME_FAILED;
            goto cleanup;
        }
    }

    res = file_size(fname);

cleanup:
    ogg_packet_clear(&codebooks);
    cleanup(in, out, &oy, vendor);
    if (res < OGGEDIT_OK)
        unlink(tempname);
    return res;
}