view sidlib.c @ 85:4c0ecb078591

Rename various variables and functions and change relevant places to use the new th_ctx API.
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 11 Feb 2016 23:18:58 +0200
parents d14c82880141
children e1ff9cd27a84
line wrap: on
line source

/*
 * SIDInfoLib - Way too simplistic PSID/RSID file library
 * Written by Matti 'ccr' H�m�l�inen <ccr@tnsp.org>
 * (C) Copyright 2014-2016 Tecnic Software productions (TNSP)
 */
#include "sidlib.h"
#include "th_endian.h"
#include "th_string.h"


static void si_append_hash16(th_md5state_t *state, uint16_t data)
{
    uint8_t ib8[2];
    ib8[0] = data & 0xff;
    ib8[1] = data >> 8;
    th_md5_append(state, (uint8_t *) &ib8, sizeof(ib8));
}


int si_read_sid_file(th_ioctx *ctx, PSIDHeader *psid)
{
    th_md5state_t state;
    uint8_t tmp8, *data = NULL;
    int index, ret = -1;
    size_t read;
    BOOL first;

    memset(psid, 0, sizeof(*psid));

    if ((data = (uint8_t *) th_malloc(PSID_BUFFER_SIZE)) == NULL)
    {
        th_io_error(ctx, THERR_MALLOC,
            "Error allocating temporary data buffer of %d bytes.\n",
            PSID_BUFFER_SIZE);
        goto error;
    }

    // Read PSID header in
    if (!thfread_str(ctx, (uint8_t *) psid->magic, PSID_MAGIC_LEN) ||
        !thfread_be16(ctx, &psid->version) ||
        !thfread_be16(ctx, &psid->dataOffset) ||
        !thfread_be16(ctx, &psid->loadAddress) ||
        !thfread_be16(ctx, &psid->initAddress) ||
        !thfread_be16(ctx, &psid->playAddress) ||
        !thfread_be16(ctx, &psid->nSongs) ||
        !thfread_be16(ctx, &psid->startSong) ||
        !thfread_be32(ctx, &psid->speed))
    {
        th_io_error(ctx, ctx->errno,
            "Could not read PSID/RSID header: %s.\n",
            th_error_str(ctx->errno));
        goto error;
    }

    psid->magic[PSID_MAGIC_LEN] = 0;

    if ((psid->magic[0] != 'R' && psid->magic[0] != 'P') ||
        psid->magic[1] != 'S' || psid->magic[2] != 'I' || psid->magic[3] != 'D' ||
        psid->version < 1 || psid->version > 3)
    {
        th_io_error(ctx, THERR_NOT_SUPPORTED,
            "Not a supported PSID or RSID file.\n");
        goto error;
    }

    psid->isRSID = psid->magic[0] == 'R';

    if (!thfread_str(ctx, (uint8_t *) psid->sidName, PSID_STR_LEN) ||
        !thfread_str(ctx, (uint8_t *) psid->sidAuthor, PSID_STR_LEN) ||
        !thfread_str(ctx, (uint8_t *) psid->sidCopyright, PSID_STR_LEN))
    {
        th_io_error(ctx, ctx->errno,
            "Error reading SID file header: %s.\n",
            th_error_str(ctx->errno));
        goto error;
    }

    psid->sidName[PSID_STR_LEN] = 0;
    psid->sidAuthor[PSID_STR_LEN] = 0;
    psid->sidCopyright[PSID_STR_LEN] = 0;

    // Check if we need to load PSIDv2NG header ...
    if (psid->version >= 2)
    {
        // Yes, we need to
        if (!thfread_be16(ctx, &psid->flags) ||
            !thfread_byte(ctx, &psid->startPage) ||
            !thfread_byte(ctx, &psid->pageLength) ||
            !thfread_be16(ctx, &psid->reserved))
        {
            th_io_error(ctx, ctx->errno,
                "Error reading PSID/RSID v2+ extra header data: %s.\n",
                th_error_str(ctx->errno));
            goto error;
        }
    }

    // Initialize MD5-hash calculation
    th_md5_init(&state);

    // Process actual data
    psid->dataSize = 0;
    first = TRUE;
    do
    {
        read = thfread(data, sizeof(uint8_t), PSID_BUFFER_SIZE, ctx);
        psid->dataSize += read;

        if (first && psid->loadAddress == 0)
        {
            if (read < 4)
            {
                th_io_error(ctx, THERR_FREAD,
                    "Error reading song data, unexpectedly small file.\n");
                goto error;
            }

            // Grab the actual load address
            psid->loadAddress = TH_LE16_TO_NATIVE(*(uint16_t *) data);

            // Strip load address (2 first bytes)
            th_md5_append(&state, &data[2], read - 2);
            first = FALSE;
        }
        else
        if (read > 0)
        {
            // Append "as is"
            th_md5_append(&state, data, read);
        }
    } while (read > 0 && !thfeof(ctx));

    // Append header data to hash
    si_append_hash16(&state, psid->initAddress);
    si_append_hash16(&state, psid->playAddress);
    si_append_hash16(&state, psid->nSongs);

    // Append song speed data to hash
    tmp8 = psid->isRSID ? 60 : 0;
    for (index = 0; index < psid->nSongs && index < 32; index++)
    {
        if (psid->isRSID)
            tmp8 = 60;
        else
            tmp8 = (psid->speed & (1 << index)) ? 60 : 0;

        th_md5_append(&state, &tmp8, sizeof(tmp8));
    }

    // Rest of songs (more than 32)
    for (index = 32; index < psid->nSongs; index++)
        th_md5_append(&state, &tmp8, sizeof(tmp8));

    // PSIDv2NG specific
    if (psid->version >= 2)
    {
        // REFER TO SIDPLAY HEADERS FOR MORE INFORMATION
        tmp8 = (psid->flags >> 2) & 3;
        if (tmp8 == 2)
            th_md5_append(&state, &tmp8, sizeof(tmp8));
    }

    // Calculate the hash
    th_md5_finish(&state, psid->hash);
    ret = 0;

error:
    // Free buffer
    th_free(data);
    return ret;
}


const char *si_get_sid_clock_str(const int flags)
{
    switch (flags & PSF_CLOCK_MASK)
    {
        case PSF_CLOCK_UNKNOWN : return "Unknown";
        case PSF_CLOCK_PAL     : return "PAL 50Hz";
        case PSF_CLOCK_NTSC    : return "NTSC 60Hz";
        case PSF_CLOCK_ANY     : return "PAL / NTSC";
        default                : return "?";
    }
}


const char *si_get_sid_model_str(const int flags)
{
    switch (flags & PSF_MODEL_MASK)
    {
        case PSF_MODEL_UNKNOWN : return "Unknown";
        case PSF_MODEL_MOS6581 : return "MOS6581";
        case PSF_MODEL_MOS8580 : return "MOS8580";
        case PSF_MODEL_ANY     : return "MOS6581 / MOS8580";
        default                : return "?";
    }
}