////////////////////////////////////////////////////////////////////////////
//                           **** WAVPACK ****                            //
//                  Hybrid Lossless Wavefile Compressor                   //
//              Copyright (c) 1998 - 2003 Conifer Software.               //
//                          All Rights Reserved.                          //
//      Distributed under the BSD Software License (see license.txt)      //
////////////////////////////////////////////////////////////////////////////

// metadata.c

// This module handles the metadata structure introduced in WavPack 4.0

#include "wavpack.h"

#include <string.h>

int read_metadata_buff (WavpackContext *wpc, WavpackMetadata *wpmd)
{
    uint32_t bytes_to_read;
    uchar tchar;

    if (wpc->stream.block_bytes_left < 2 || !wpc->infile (&wpmd->id, 1) || !wpc->infile (&tchar, 1))
        return FALSE;

    wpmd->byte_length = tchar << 1;
    wpc->stream.block_bytes_left -= 2;

    if (wpmd->id & ID_LARGE) {
        wpmd->id &= ~ID_LARGE;

        if (wpc->stream.block_bytes_left < 2 || !wpc->infile (&tchar, 1))
            return FALSE;

        wpmd->byte_length += (int32_t) tchar << 9; 

        if (!wpc->infile (&tchar, 1))
            return FALSE;

        wpmd->byte_length += (int32_t) tchar << 17;
        wpc->stream.block_bytes_left -= 2;
    }

    if ((wpc->stream.block_bytes_left -= wpmd->byte_length) < 0)
        return FALSE;

    if (wpmd->id & ID_ODD_SIZE) {
        wpmd->id &= ~ID_ODD_SIZE;
        wpmd->byte_length--;
    }

    if (!wpmd->byte_length || wpmd->id == ID_WV_BITSTREAM) {
        wpmd->data = NULL;
        return TRUE;
    }

    bytes_to_read = wpmd->byte_length + (wpmd->byte_length & 1);

    if (bytes_to_read > sizeof (wpc->read_buffer)) {
        wpmd->data = NULL;

        while (bytes_to_read > sizeof (wpc->read_buffer))
            if (wpc->infile (wpc->read_buffer, sizeof (wpc->read_buffer)) == sizeof (wpc->read_buffer))
                bytes_to_read -= sizeof (wpc->read_buffer);
            else
                return FALSE;
    }
    else
        wpmd->data = wpc->read_buffer;

    if (bytes_to_read && wpc->infile (wpc->read_buffer, bytes_to_read) != (int32_t) bytes_to_read) {
        wpmd->data = NULL;
        return FALSE;
    }

    return TRUE;
}

int process_metadata (WavpackContext *wpc, WavpackMetadata *wpmd)
{
    WavpackStream *wps = &wpc->stream;

    switch (wpmd->id) {
        case ID_DUMMY:
            return TRUE;

        case ID_DECORR_TERMS:
            return read_decorr_terms (wps, wpmd);

        case ID_DECORR_WEIGHTS:
            return read_decorr_weights (wps, wpmd);

        case ID_DECORR_SAMPLES:
            return read_decorr_samples (wps, wpmd);

        case ID_ENTROPY_VARS:
            return read_entropy_vars (wps, wpmd);

        case ID_HYBRID_PROFILE:
            return read_hybrid_profile (wps, wpmd);

        case ID_FLOAT_INFO:
            return read_float_info (wps, wpmd);

        case ID_INT32_INFO:
            return read_int32_info (wps, wpmd);

        case ID_CHANNEL_INFO:
            return read_channel_info (wpc, wpmd);

        case ID_SAMPLE_RATE:
            return read_sample_rate (wpc, wpmd);

        case ID_CONFIG_BLOCK:
            return read_config_info (wpc, wpmd);

        case ID_WV_BITSTREAM:
            return init_wv_bitstream (wpc, wpmd);

        case ID_SHAPING_WEIGHTS:
        case ID_WVC_BITSTREAM:
        case ID_WVX_BITSTREAM:
            return TRUE;

        default:
            return (wpmd->id & ID_OPTIONAL_DATA) ? TRUE : FALSE;
    }
}

int copy_metadata (WavpackMetadata *wpmd, uchar *buffer_start, uchar *buffer_end)
{
    uint32_t mdsize = wpmd->byte_length + (wpmd->byte_length & 1);
    WavpackHeader *wphdr = (WavpackHeader *) buffer_start;

    if (wpmd->byte_length & 1)
        ((char *) wpmd->data) [wpmd->byte_length] = 0;

    mdsize += (wpmd->byte_length > 510) ? 4 : 2;
    buffer_start += wphdr->ckSize + 8;

    if (buffer_start + mdsize >= buffer_end)
        return FALSE;

    buffer_start [0] = wpmd->id | (wpmd->byte_length & 1 ? ID_ODD_SIZE : 0);
    buffer_start [1] = (wpmd->byte_length + 1) >> 1;

    if (wpmd->byte_length > 510) {
        buffer_start [0] |= ID_LARGE;
        buffer_start [2] = (wpmd->byte_length + 1) >> 9;
        buffer_start [3] = (wpmd->byte_length + 1) >> 17;
    }

    if (wpmd->data && wpmd->byte_length) {
        if (wpmd->byte_length > 510) {
            buffer_start [0] |= ID_LARGE;
            buffer_start [2] = (wpmd->byte_length + 1) >> 9;
            buffer_start [3] = (wpmd->byte_length + 1) >> 17;
            memcpy (buffer_start + 4, wpmd->data, mdsize - 4);
        }
        else
            memcpy (buffer_start + 2, wpmd->data, mdsize - 2);
    }

    wphdr->ckSize += mdsize;
    return TRUE;
}

void free_metadata (WavpackMetadata *wpmd)
{
    wpmd->data = NULL;
}

