view src/xs_support.c @ 957:0e60e5d56fdd

Change how the backend emulator library is initialized for libSIDPlay2 and FP, as it seems the engine configuration has some persistence despite reconfiguration between loaded files if same engine object is retained. This caused, for example, 2SID stereo tunes being played "mono" if played after a normal 1-SID tune. Duh.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 20 Nov 2012 22:13:48 +0200
parents 3c2efa18c422
children 0233c5fd7d5e
line wrap: on
line source

/*  
   XMMS-SID - SIDPlay input plugin for X MultiMedia System (XMMS)

   Miscellaneous support functions
   
   Programmed and designed by Matti 'ccr' Hamalainen <ccr@tnsp.org>
   (C) Copyright 1999-2009 Tecnic Software productions (TNSP)

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

   --
   Except for the MD5 related functionality at the end of this file.
   --
*/
#include "xs_support.h"
#ifndef __AUDACIOUS_NEWVFS__
#include <sys/types.h>
#include <sys/stat.h>
#endif


gboolean xs_fread_str(XSFile *f, void *buf, const size_t len)
{
    return xs_fread(buf, len, 1, f) == 1;
}


gboolean xs_fread_byte(XSFile *f, guint8 *val)
{
    gint tmp = xs_fgetc(f);
    *val = tmp;
    return tmp != EOF;
}


/* Error messages
 */
void xs_error(const char *fmt, ...)
{
    va_list ap;
    fprintf(stderr, "XMMS-SID: ");
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
}

#ifndef DEBUG_NP
void XSDEBUG(const char *fmt, ...)
{
#ifdef DEBUG
    va_list ap;
    fprintf(stderr, "XSDEBUG: ");
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
#else
    (void) fmt;
#endif
}
#endif


#ifndef __AUDACIOUS_NEWVFS__
off_t xs_fsize(XSFile *f)
{
    off_t pos;
    if (fseeko(f, 0, SEEK_END) < 0)
        return -1;

    pos = ftello(f);

    if (fseeko(f, 0, SEEK_SET) < 0)
        return -1;

    return pos;
}
#endif


gboolean xs_is_dir_path(const gchar *path)
{
#ifndef __AUDACIOUS_NEWVFS__
    struct stat sb;
    if (stat(path, &sb) < 0)
        return FALSE;

    return (S_ISDIR(sb.st_mode));
#else
    // XXX
#endif
}


gchar *xs_get_dir_path(const gchar *path)
{
#ifndef __AUDACIOUS_NEWVFS__
    if (!xs_is_dir_path(path))
        return g_dirname(path);
    else
        return g_strdup(path);
#else
    // XXX
#endif
}


gboolean xs_fread_be16(XSFile *f, guint16 *val)
{
    guint16 result;
    if (xs_fread(&result, sizeof(result), 1, f) != 1)
        return FALSE;
    *val = GUINT16_FROM_BE(result);
    return TRUE;
}


gboolean xs_fread_be32(XSFile *f, guint32 *val)
{
    guint32 result;
    if (xs_fread(&result, sizeof(result), 1, f) != 1)
        return FALSE;
    *val = GUINT32_FROM_BE(result);
    return TRUE;
}


/* Load a file to a buffer, return 0 on success, negative value on error
 */
gboolean xs_fload_buffer(const gchar *filename,
    guint8 **pbuf, size_t *bufSize, const size_t maxSize, gboolean failMaxSize)
{
    XSFile *fp = NULL;
    size_t readSize = 0, fileSize = 0;
    gboolean res = FALSE;

    if (filename == NULL)
        return FALSE;

    if ((fp = xs_fopen(filename, "rb")) == NULL)
    {
        xs_error("Could not open '%s' for reading.\n", filename);
        goto error;
    }

    fileSize = xs_fsize(fp);
    if (failMaxSize && fileSize > maxSize)
    {
        xs_error("File '%s' size %d exceeds maxSize %d.\n",
            filename, fileSize, maxSize);
        goto error;
    }

    readSize = fileSize < maxSize ? fileSize : maxSize;

    if ((*pbuf = (guint8 *) g_malloc(readSize * sizeof(guint8))) == NULL)
    {
        xs_error("Could not allocate %d bytes for filebuffer '%s'.\n",
            readSize, filename);
        goto error;
    }

    *bufSize = xs_fread(*pbuf, sizeof(guint8), readSize, fp);
    res = (readSize == *bufSize);

error:
    if (fp != NULL)
        xs_fclose(fp);

    if (!res)
    {
        xs_error("File '%s', expected %d bytes, read %d bytes.\n",
            filename, readSize, *bufSize);
    }

    return res;
}


gboolean xs_fload_buffer_path(const gchar *ppath, const gchar *pfilename,
    guint8 **pbuf, size_t *bufSize, const size_t maxSize, gboolean failMaxSize)
{
    gchar *filename, *pseparator;
    gboolean res;

    if (pfilename == NULL)
        return FALSE;

    pseparator = ppath != NULL ? ((ppath[strlen(ppath)] != '/') ? "/" : "") : "";
    filename = g_strdup_printf("%s%s%s", ppath, pseparator, pfilename);

    if (filename == NULL)
        return FALSE;

    res = xs_fload_buffer(filename, pbuf, bufSize, maxSize, failMaxSize);

    g_free(filename);
    return res;
}


/*
 * MD5 implementation, modified for XMMS-SID from
 * Colin Plumb's implementation by Matti 'ccr' Hämäläinen.
 *
 * This code implements the MD5 message-digest algorithm.
 * The algorithm is due to Ron Rivest.  This code was
 * written by Colin Plumb in 1993, no copyright is claimed.
 * This code is in the public domain; do with it what you wish.
 */

#if G_BYTE_ORDER == G_LITTLE_ENDIAN
#  define xs_md5_bytereverse(buf, len)    /* Nothing */
#elif G_BYTE_ORDER == G_BIG_ENDIAN
static void xs_md5_bytereverse(guint8 *buf, guint l)
{
    guint32 t;
    do
    {
        t = (guint32) ((guint) buf[3] << 8 | buf[2]) << 16 | ((guint) buf[1] << 8 | buf[0]);
        *(guint32 *) buf = t;
        buf += sizeof(guint32);
    } while (--l);
}
#else
#  error Unsupported endianess!
#endif


/* Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
 * initialization constants.
 */
void xs_md5_init(xs_md5state_t *ctx)
{
    ctx->buf[0] = 0x67452301;
    ctx->buf[1] = 0xefcdab89;
    ctx->buf[2] = 0x98badcfe;
    ctx->buf[3] = 0x10325476;

    ctx->bits[0] = 0;
    ctx->bits[1] = 0;
}


/* The core of the MD5 algorithm, this alters an existing MD5 hash to
 * reflect the addition of 16 longwords of new data.  xs_md5_update blocks
 * the data and converts bytes into longwords for this routine.
 */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
#define MD5STEP(f, w, x, y, z, data, s) ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )

static void xs_md5_transform(guint32 buf[4], guint32 const in[16])
{
    register guint32 a, b, c, d;

    a = buf[0];
    b = buf[1];
    c = buf[2];
    d = buf[3];

    MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7);
    MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12);
    MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17);
    MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22);
    MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7);
    MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12);
    MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17);
    MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22);
    MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7);
    MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12);
    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);

    MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5);
    MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9);
    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
    MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20);
    MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5);
    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
    MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20);
    MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5);
    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
    MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14);
    MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20);
    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
    MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9);
    MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14);
    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);

    MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4);
    MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11);
    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
    MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4);
    MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11);
    MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16);
    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
    MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11);
    MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16);
    MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23);
    MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4);
    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
    MD5STEP(F3, b, c, d, a, in[ 2] + 0xc4ac5665, 23);

    MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6);
    MD5STEP(F4, d, a, b, c, in[ 7] + 0x432aff97, 10);
    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
    MD5STEP(F4, b, c, d, a, in[ 5] + 0xfc93a039, 21);
    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
    MD5STEP(F4, d, a, b, c, in[ 3] + 0x8f0ccc92, 10);
    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
    MD5STEP(F4, b, c, d, a, in[ 1] + 0x85845dd1, 21);
    MD5STEP(F4, a, b, c, d, in[ 8] + 0x6fa87e4f, 6);
    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
    MD5STEP(F4, c, d, a, b, in[ 6] + 0xa3014314, 15);
    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
    MD5STEP(F4, a, b, c, d, in[ 4] + 0xf7537e82, 6);
    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
    MD5STEP(F4, c, d, a, b, in[ 2] + 0x2ad7d2bb, 15);
    MD5STEP(F4, b, c, d, a, in[ 9] + 0xeb86d391, 21);

    buf[0] += a;
    buf[1] += b;
    buf[2] += c;
    buf[3] += d;
}


/* Update context to reflect the concatenation of another buffer full
 * of bytes.
 */
void xs_md5_append(xs_md5state_t *ctx, const guint8 *buf, guint len)
{
    guint32 t;

    /* Update bitcount */
    t = ctx->bits[0];
    if ((ctx->bits[0] = t + ((guint32) len << 3)) < t)
        ctx->bits[1]++;    /* Carry from low to high */
    ctx->bits[1] += len >> 29;

    t = (t >> 3) & 0x3f;    /* Bytes already in shsInfo->data */

    /* Handle any leading odd-sized chunks */
    if (t)
    {
        guint8 *p = (guint8 *) ctx->in + t;

        t = 64 - t;
        if (len < t)
        {
            memcpy(p, buf, len);
            return;
        }
        memcpy(p, buf, t);
        xs_md5_bytereverse(ctx->in, 16);
        xs_md5_transform(ctx->buf, (guint32 *) ctx->in);
        buf += t;
        len -= t;
    }

    /* Process data in 64-byte chunks */
    while (len >= 64)
    {
        memcpy(ctx->in, buf, 64);
        xs_md5_bytereverse(ctx->in, 16);
        xs_md5_transform(ctx->buf, (guint32 *) ctx->in);
        buf += 64;
        len -= 64;
    }

    /* Handle any remaining bytes of data. */
    memcpy(ctx->in, buf, len);
}


/* Final wrapup - pad to 64-byte boundary with the bit pattern 
 * 1 0* (64-bit count of bits processed, MSB-first)
 */
void xs_md5_finish(xs_md5state_t *ctx, xs_md5hash_t digest)
{
    guint count;
    guint8 *p;

    /* Compute number of bytes mod 64 */
    count = (ctx->bits[0] >> 3) & 0x3F;

    /* Set the first char of padding to 0x80.  This is safe since there is
       always at least one byte free */
    p = ctx->in + count;
    *p++ = 0x80;

    /* Bytes of padding needed to make 64 bytes */
    count = 64 - 1 - count;

    /* Pad out to 56 mod 64 */
    if (count < 8)
    {
        /* Two lots of padding:  Pad the first block to 64 bytes */
        memset(p, 0, count);
        xs_md5_bytereverse(ctx->in, 16);
        xs_md5_transform(ctx->buf, (guint32 *) ctx->in);

        /* Now fill the next block with 56 bytes */
        memset(ctx->in, 0, 56);
    }
    else
    {
        /* Pad block to 56 bytes */
        memset(p, 0, count - 8);
    }
    xs_md5_bytereverse(ctx->in, 14);

    /* Append length in bits and transform */
    memcpy(((guint32 *) ctx->in) + 14, &ctx->bits[0], sizeof(guint32));
    memcpy(((guint32 *) ctx->in) + 15, &ctx->bits[1], sizeof(guint32));

    xs_md5_transform(ctx->buf, (guint32 *) ctx->in);
    xs_md5_bytereverse((guint8 *) ctx->buf, 4);
    memcpy(digest, ctx->buf, 16);
    memset(ctx, 0, sizeof(*ctx));
}


/* Copy a given string over in *result.
 */
gint xs_pstrcpy(gchar **result, const gchar *str)
{
    /* Check the string pointers */
    if (!result || !str)
        return -1;

    /* Allocate memory for destination */
    g_free(*result);
    
    *result = (gchar *) g_malloc(strlen(str) + 1);
    if (!*result)
        return -2;

    /* Copy to the destination */
    strcpy(*result, str);

    return 0;
}


/* Concatenates a given string into string pointed by *result.
 */
gint xs_pstrcat(gchar **result, const gchar *str)
{
    /* Check the string pointers */
    if (!result || !str)
        return -1;

    if (*result != NULL)
    {
        *result = (gchar *) g_realloc(*result, strlen(*result) + strlen(str) + 1);
        if (*result == NULL)
            return -1;
        strcat(*result, str);
    }
    else
    {
        *result = (gchar *) g_malloc(strlen(str) + 1);
        if (*result == NULL)
            return -1;
        strcpy(*result, str);
    }

    return 0;
}


/* Concatenate a given string up to given dest size or \n.
 * If size max is reached, change the end to "..."
 */
void xs_pnstrcat(gchar *dest, const size_t size, const gchar *str)
{
    size_t i, n;
    const gchar *s;
    gchar *d;

    for (d = dest, i = 0; *d && i < size; i++, d++);

    s = str;
    while (*s && *s != '\n' && i < size)
    {
        *d = *s;
        d++;
        s++;
        i++;
    }

    *d = 0;

    if (i >= size)
    {
        i--;
        d--;
        for (n = 3; i > 0 && n > 0; d--, i--, n--)
            *d = '.';
    }
}


/* Locate character in string
 */
void xs_findnext(const gchar *str, size_t *pos)
{
    while (str[*pos] && isspace(str[*pos]))
        (*pos)++;
}


void xs_findeol(const gchar *str, size_t *pos)
{
    while (str[*pos] && (str[*pos] != '\n') && (str[*pos] != '\r'))
        (*pos)++;
}


void xs_findnum(const gchar *str, size_t *pos)
{
    while (str[*pos] && isdigit(str[*pos]))
        (*pos)++;
}