780 lines · 23.7 KB · C
RawDownload
/*  _______         ____    __         ___    ___ * \    _  \       \    /  \  /       \   \  /   /       '   '  ' *  |  | \  \       |  |    ||         |   \/   |         .      . *  |  |  |  |      |  |    ||         ||\  /|  | *  |  |  |  |      |  |    ||         || \/ |  |         '  '  ' *  |  |  |  |      |  |    ||         ||    |  |         .      . *  |  |_/  /        \  \__//          ||    |  | * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque *                                                      /  \ *                                                     / .  \ * reads3m.c - Code to read a ScreamTracker 3         / / \  \ *             module from an open file.             | <  /   \_ *                                                   |  \/ /\   / * By entheh.                                         \_  /  > / *                                                      | \ / / *                                                      |  ' / *                                                       \__/ */ // IT_STEREO... :o#include <stdlib.h>#include <string.h> #include "dumb.h"#include "internal/it.h" static int it_s3m_read_sample_header(IT_SAMPLE *sample, long *offset,                                     unsigned char *pack, int cwtv,                                     DUMBFILE *f) {    unsigned char type;    int flags;     type = dumbfile_getc(f);     dumbfile_getnc((char *)sample->filename, 12, f);    sample->filename[12] = 0;     if (type > 1) {        /** WARNING: no adlib support */        dumbfile_skip(f, 3 + 12 + 1 + 1 + 2 + 2 + 2 + 12);        dumbfile_getnc((char *)sample->name, 28, f);        sample->name[28] = 0;        dumbfile_skip(f, 4);        sample->flags &= ~IT_SAMPLE_EXISTS;        return dumbfile_error(f);    }     *offset = dumbfile_getc(f) << 20;    *offset += dumbfile_igetw(f) << 4;     sample->length = dumbfile_igetl(f);    sample->loop_start = dumbfile_igetl(f);    sample->loop_end = dumbfile_igetl(f);     sample->default_volume = dumbfile_getc(f);     dumbfile_skip(f, 1);     flags = dumbfile_getc(f);     if (flags < 0 || (flags != 0 && flags != 4))        /* Sample is packed apparently (or error reading from file). We don't         * know how to read packed samples.         */        return -1;     *pack = flags;     flags = dumbfile_getc(f);     sample->C5_speed = dumbfile_igetl(f) << 1;     /* Skip four unused bytes and three internal variables. */    dumbfile_skip(f, 4 + 2 + 2 + 4);     dumbfile_getnc((char *)sample->name, 28, f);    sample->name[28] = 0;     if (type == 0 || sample->length <= 0) {        /* Looks like no-existy. Anyway, there's for sure no 'SCRS' ... */        sample->flags &= ~IT_SAMPLE_EXISTS;        return dumbfile_error(f);    }     if (dumbfile_mgetl(f) != DUMB_ID('S', 'C', 'R', 'S'))        return -1;     sample->global_volume = 64;     sample->flags = IT_SAMPLE_EXISTS;    if (flags & 1)        sample->flags |= IT_SAMPLE_LOOP;     /* The ST3 TECH.DOC is unclear on this, but IMAGO Orpheus is not. Piece of     * crap. */     if (flags & 2) {        sample->flags |= IT_SAMPLE_STEREO;         if ((cwtv & 0xF000) == 0x2000) {            sample->length >>= 1;            sample->loop_start >>= 1;            sample->loop_end >>= 1;        }    }     if (flags & 4) {        sample->flags |= IT_SAMPLE_16BIT;         if ((cwtv & 0xF000) == 0x2000) {            sample->length >>= 1;            sample->loop_start >>= 1;            sample->loop_end >>= 1;        }    }     sample->default_pan = 0; // 0 = don't use, or 160 = centre?     if (sample->flags & IT_SAMPLE_LOOP) {        if ((unsigned int)sample->loop_end > (unsigned int)sample->length)            /*sample->flags &= ~IT_SAMPLE_LOOP;*/            sample->loop_end = sample->length;        else if ((unsigned int)sample->loop_start >=                 (unsigned int)sample->loop_end)            sample->flags &= ~IT_SAMPLE_LOOP;        else            /* ScreamTracker seems not to save what comes after the loop end             * point, but rather to assume it is a duplicate of what comes at             * the loop start point. I am not completely sure of this though.             * It is easy to evade; simply truncate the sample.             */            sample->length = sample->loop_end;    }     // Do we need to set all these?    sample->vibrato_speed = 0;    sample->vibrato_depth = 0;    sample->vibrato_rate = 0;    sample->vibrato_waveform = IT_VIBRATO_SINE;    sample->finetune = 0;    sample->max_resampling_quality = -1;     return dumbfile_error(f);} static int it_s3m_read_sample_data(IT_SAMPLE *sample, int ffi,                                   unsigned char pack, DUMBFILE *f) {    long n;     long datasize = sample->length;    if (sample->flags & IT_SAMPLE_STEREO)        datasize <<= 1;     sample->data = malloc(datasize * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1));    if (!sample->data)        return -1;     if (pack == 4) {        if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0)            return -1;    } else if (sample->flags & IT_SAMPLE_STEREO) {        if (sample->flags & IT_SAMPLE_16BIT) {            for (n = 0; n < datasize; n += 2)                ((short *)sample->data)[n] = dumbfile_igetw(f);            for (n = 1; n < datasize; n += 2)                ((short *)sample->data)[n] = dumbfile_igetw(f);        } else {            for (n = 0; n < datasize; n += 2)                ((signed char *)sample->data)[n] = dumbfile_getc(f);            for (n = 1; n < datasize; n += 2)                ((signed char *)sample->data)[n] = dumbfile_getc(f);        }    } else if (sample->flags & IT_SAMPLE_16BIT)        for (n = 0; n < sample->length; n++)            ((short *)sample->data)[n] = dumbfile_igetw(f);    else        for (n = 0; n < sample->length; n++)            ((signed char *)sample->data)[n] = dumbfile_getc(f);     if (dumbfile_error(f))        return -1;     if (ffi != 1) {        /* Convert to signed. */        if (sample->flags & IT_SAMPLE_16BIT)            for (n = 0; n < datasize; n++)                ((short *)sample->data)[n] ^= 0x8000;        else            for (n = 0; n < datasize; n++)                ((signed char *)sample->data)[n] ^= 0x80;    }     return 0;} static int it_s3m_read_pattern(IT_PATTERN *pattern, DUMBFILE *f,                               unsigned char *buffer) {    int length;    int buflen = 0;    int bufpos = 0;     IT_ENTRY *entry;     unsigned char channel;     /* Haha, this is hilarious!     *     * Well, after some experimentation, it seems that different S3M writers     * define the format in different ways. The S3M docs say that the first     * two bytes hold the "length of [the] packed pattern", and the packed     * pattern data follow. Judging by the contents of ARMANI.S3M, packaged     * with ScreamTracker itself, the measure of length _includes_ the two     * bytes used to store the length; in other words, we should read     * (length - 2) more bytes. However, aryx.s3m, packaged with ModPlug     * Tracker, excludes these two bytes, so (length) more bytes must be     * read.     *     * Call me crazy, but I just find it insanely funny that the format was     * misunderstood in this way :D     *     * Now we can't just risk reading two extra bytes, because then we     * overshoot, and DUMBFILEs don't support backward seeking (for a good     * reason). Luckily, there is a way. We can read the data little by     * little, and stop when we have 64 rows in memory. Provided we protect     * against buffer overflow, this method should work with all sensibly     * written S3M files. If you find one for which it does not work, please     * let me know at entheh@users.sf.net so I can look at it.     *     * "for a good reason" ? What's this nonsense? -kode54     *     */     length = dumbfile_igetw(f);     if (dumbfile_error(f) || !length)        return -1;     pattern->n_rows = 0;    pattern->n_entries = 0;     /* Read in the pattern data, little by little, and work out how many     * entries we need room for. Sorry, but this is just so funny...     */    for (;;) {        unsigned char b = buffer[buflen++] = dumbfile_getc(f); #if 1        static const unsigned char used[8] = {0, 2, 1, 3, 2, 4, 3, 5};        channel = b & 31;        b >>= 5;        pattern->n_entries++;        if (b) {            if (buflen + used[b] >= 65536)                return -1;            if (buflen + used[b] <= length)                dumbfile_getnc((char *)buffer + buflen, used[b], f);            else                memset(buffer + buflen, 0, used[b]);            buflen += used[b];        } else {            /* End of row */            if (++pattern->n_rows == 64)                break;            if (buflen >= 65536)                return -1;        }#else        if (b == 0) {            /* End of row */            pattern->n_entries++;            if (++pattern->n_rows == 64)                break;            if (buflen >= 65536)                return -1;        } else {            static const unsigned char used[8] = {0, 2, 1, 3, 2, 4, 3, 5};            channel = b & 31;            b >>= 5;            if (b) {                pattern->n_entries++;                if (buflen + used[b] >= 65536)                    return -1;                dumbfile_getnc(buffer + buflen, used[b], f);                buflen += used[b];            }        }#endif         /* We have ensured that buflen < 65536 at this point, so it is safe         * to iterate and read at least one more byte without checking.         * However, now would be a good time to check for errors reading from         * the file.         */         if (dumbfile_error(f))            return -1;         /* Great. We ran out of data, but there should be data for more rows.         * Fill the rest with null data...         */        if (buflen >= length && pattern->n_rows < 64) {            while (pattern->n_rows < 64) {                if (buflen >= 65536)                    return -1;                buffer[buflen++] = 0;                pattern->n_entries++;                pattern->n_rows++;            }            break;        }    }     pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));     if (!pattern->entry)        return -1;     entry = pattern->entry;     while (bufpos < buflen) {        unsigned char b = buffer[bufpos++]; #if 1        if (!(b & ~31))#else        if (b == 0)#endif        {            /* End of row */            IT_SET_END_ROW(entry);            entry++;            continue;        }         channel = b & 31;         if (b & 224) {            entry->mask = 0;            entry->channel = channel;             if (b & 32) {                unsigned char n = buffer[bufpos++];                if (n != 255) {                    if (n == 254)                        entry->note = IT_NOTE_CUT;                    else                        entry->note = (n >> 4) * 12 + (n & 15);                    entry->mask |= IT_ENTRY_NOTE;                }                 entry->instrument = buffer[bufpos++];                if (entry->instrument)                    entry->mask |= IT_ENTRY_INSTRUMENT;            }             if (b & 64) {                entry->volpan = buffer[bufpos++];                if (entry->volpan != 255)                    entry->mask |= IT_ENTRY_VOLPAN;            }             if (b & 128) {                entry->effect = buffer[bufpos++];                entry->effectvalue = buffer[bufpos++];                // XXX woot                if (entry->effect && entry->effect < IT_MIDI_MACRO /*!= 255*/) {                    entry->mask |= IT_ENTRY_EFFECT;                    switch (entry->effect) {                    case IT_BREAK_TO_ROW:                        entry->effectvalue -= (entry->effectvalue >> 4) * 6;                        break;                     case IT_SET_CHANNEL_VOLUME:                    case IT_CHANNEL_VOLUME_SLIDE:                    case IT_PANNING_SLIDE:                    case IT_GLOBAL_VOLUME_SLIDE:                    case IT_PANBRELLO:                    case IT_MIDI_MACRO:                        entry->mask &= ~IT_ENTRY_EFFECT;                        break;                     case IT_S:                        switch (entry->effectvalue >> 4) {                        case IT_S_SET_PANBRELLO_WAVEFORM:                        case IT_S_FINE_PATTERN_DELAY:                        case IT_S7:                        case IT_S_SET_SURROUND_SOUND:                        case IT_S_SET_MIDI_MACRO:                            entry->mask &= ~IT_ENTRY_EFFECT;                            break;                        }                        break;                    }                }                /** WARNING: ARGH! CONVERT TEH EFFECTS!@~ */            }             entry++;        }    }     ASSERT(entry == pattern->entry + pattern->n_entries);     return 0;} /** WARNING: this is duplicated in itread.c - also bad practice to use the same * struct name unless they are unified in a header *//* Currently we assume the sample data are stored after the sample headers in * module files. This assumption may be unjustified; let me know if you have * trouble. */ #define S3M_COMPONENT_INSTRUMENT 1#define S3M_COMPONENT_PATTERN 2#define S3M_COMPONENT_SAMPLE 3 typedef struct S3M_COMPONENT {    unsigned char type;    unsigned char n;    long offset;    short sampfirst; /* component[sampfirst] = first sample data after this */    short sampnext; /* sampnext is used to create linked lists of sample data */} S3M_COMPONENT; static int s3m_component_compare(const void *e1, const void *e2) {    return (int)(((const S3M_COMPONENT *)e1)->offset -                 ((const S3M_COMPONENT *)e2)->offset);} static DUMB_IT_SIGDATA *it_s3m_load_sigdata(DUMBFILE *f, int *cwtv) {    DUMB_IT_SIGDATA *sigdata;     int flags, ffi;    int default_pan_present;     int master_volume;     unsigned char sample_pack[256];     S3M_COMPONENT *component;    int n_components = 0;     int n;     unsigned char *buffer;     sigdata = malloc(sizeof(*sigdata));    if (!sigdata)        return NULL;     dumbfile_getnc((char *)sigdata->name, 28, f);    sigdata->name[28] = 0;     n = dumbfile_getc(f);     if (n != 0x1A && n != 0) {        free(sigdata);        return NULL;    }     if (dumbfile_getc(f) != 16) {        free(sigdata);        return NULL;    }     dumbfile_skip(f, 2);     sigdata->song_message = NULL;    sigdata->order = NULL;    sigdata->instrument = NULL;    sigdata->sample = NULL;    sigdata->pattern = NULL;    sigdata->midi = NULL;    sigdata->checkpoint = NULL;     sigdata->n_orders = dumbfile_igetw(f);    sigdata->n_instruments = 0;    sigdata->n_samples = dumbfile_igetw(f);    sigdata->n_patterns = dumbfile_igetw(f);     if (dumbfile_error(f) || sigdata->n_orders <= 0 ||        sigdata->n_orders > 1024 || // Whoa, nelly.        sigdata->n_samples > 256 || sigdata->n_patterns > 256) {        _dumb_it_unload_sigdata(sigdata);        return NULL;    }     sigdata->order = malloc(sigdata->n_orders);    if (!sigdata->order) {        _dumb_it_unload_sigdata(sigdata);        return NULL;    }     if (sigdata->n_samples) {        sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));        if (!sigdata->sample) {            _dumb_it_unload_sigdata(sigdata);            return NULL;        }

Lines 1–500 of 780.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t