/* * Copyright (c) 2002, 2003, 2004, 2005, 2007 by Christian Nowak * Last update: 20th October, 2007 */ #include #include #include #include "effects.h" #include "modplay.h" #include "mod.h" #define FORMAT_ID 1 #define DESCRIPTION "Amiga Pro/Soundtracker" #define AUTHOR "Christian Nowak " #define VERSION "v0.03b" #define COPYRIGHT "Copyright (c) 2002, 2003, 2007" #define SAFE_MALLOC(dest, a) \ dest = malloc(a); \ if (dest == NULL) { \ MODFILE_Free(mod); \ return -2; \ } static u32 s3m_finetunes[16] = { 7895,7941,7985,8046,8107,8169,8232,8280, 8363,8413,8463,8529,8581,8651,8723,8757 }; #define NUM_AMIGA_FREQS ((22 * 5) + 5) static const struct { u16 amigafreq; u8 st3note; } s3m_amiga2st3[] = { { 0x1ac0, 0x00 }, { 0x1940, 0x01 }, { 0x17d0, 0x02 }, { 0x1680, 0x03 }, { 0x1530, 0x04 }, { 0x1400, 0x05 }, { 0x12e0, 0x06 }, { 0x11d0, 0x07 }, { 0x10d0, 0x08 }, { 0x0fe0, 0x09 }, { 0x0f00, 0x0a }, { 0x0e2c, 0x0b }, { 0x0d60, 0x10 }, { 0x0ca0, 0x11 }, { 0x0be8, 0x12 }, { 0x0b40, 0x13 }, { 0x0a98, 0x14 }, { 0x0a00, 0x15 }, { 0x0970, 0x16 }, { 0x08e8, 0x17 }, { 0x0868, 0x18 }, { 0x07f0, 0x19 }, { 0x0780, 0x1a }, { 0x0716, 0x1b }, { 0x06b0, 0x20 }, { 0x0650, 0x21 }, { 0x05f5, 0x22 }, { 0x05a0, 0x23 }, { 0x054f, 0x24 }, { 0x0503, 0x25 }, { 0x04bb, 0x26 }, { 0x0477, 0x27 }, { 0x0436, 0x28 }, { 0x03fa, 0x29 }, { 0x03c1, 0x2a }, { 0x0386, 0x2b }, { 0x0358, 0x30 }, { 0x0328, 0x31 }, { 0x02fa, 0x32 }, { 0x02d0, 0x33 }, { 0x02a6, 0x34 }, { 0x0280, 0x35 }, { 0x025c, 0x36 }, { 0x023a, 0x37 }, { 0x021a, 0x38 }, { 0x01fc, 0x39 }, { 0x01e0, 0x3a }, { 0x01c5, 0x3b }, { 0x01ac, 0x40 }, { 0x0194, 0x41 }, { 0x017d, 0x42 }, { 0x0168, 0x43 }, { 0x0153, 0x44 }, { 0x0140, 0x45 }, { 0x012e, 0x46 }, { 0x011d, 0x47 }, { 0x010d, 0x48 }, { 0x00fe, 0x49 }, { 0x00f0, 0x4a }, { 0x00e2, 0x4b }, { 0x00d6, 0x50 }, { 0x00ca, 0x51 }, { 0x00be, 0x52 }, { 0x00b4, 0x53 }, { 0x00aa, 0x54 }, { 0x00a0, 0x55 }, { 0x0097, 0x56 }, { 0x008f, 0x57 }, { 0x0087, 0x58 }, { 0x007f, 0x59 }, { 0x0078, 0x5a }, { 0x0071, 0x5b }, { 0x006b, 0x60 }, { 0x0065, 0x61 }, { 0x005f, 0x62 }, { 0x005a, 0x63 }, { 0x0055, 0x64 }, { 0x0050, 0x65 }, { 0x004c, 0x66 }, { 0x0047, 0x67 }, { 0x0043, 0x68 }, { 0x003f, 0x69 }, { 0x003c, 0x6a }, { 0x0039, 0x6b }, { 0x0035, 0x70 }, { 0x0032, 0x71 }, { 0x002f, 0x72 }, { 0x002d, 0x73 }, { 0x002a, 0x74 }, { 0x0028, 0x75 }, { 0x0025, 0x76 }, { 0x0023, 0x77 }, { 0x0021, 0x78 }, { 0x001f, 0x79 }, { 0x001e, 0x7a }, { 0x001c, 0x7b }, { 0x001a, 0x80 }, { 0x0019, 0x81 }, { 0x0017, 0x82 }, { 0x0016, 0x83 }, { 0x0015, 0x84 }, { 0x0014, 0x85 }, { 0x0012, 0x86 }, { 0x0011, 0x87 }, { 0x0010, 0x88 }, { 0x000f, 0x89 }, { 0x000f, 0x8a }, { 0x000e, 0x8b }, { 0x004b, 0x66 }, { 0x0474, 0x27 }, { 0x0500, 0x25 }, { 0x05f4, 0x22 }, { 0x054c, 0x24 }, { 0x03f8, 0x29 }, { 0x02b4, 0x34 } }; static int getSamplesSize(MODFILE *mod) { int i, s; for (i = s = 0; i < mod->nSamples; i++) s += mod->samples[i].sampleInfo.length; return s; } static int calcNumOfPatterns(MODFILE *mod, int modlength) { int n1, n2; int i; int patternsSize = 256 * (mod->nChannels - 1); for (i = n1 = 0; i < mod->songlength; i++) { if (mod->playlist[i] > n1) n1 = mod->playlist[i]; } n1++; n2 = modlength - getSamplesSize(mod) - 1084; return n2 % patternsSize == 0 ? n2 / patternsSize : n1; } /** * int MODFILE_SetMOD(u8 *modfile, int modlength, MODFILE *mod); * * Processes the raw data of a Protracker MOD file and copies * it to a structure. The structure can then be used as a handle * of the MOD file. The original raw data isn't needed by the * handle. * * Returns a value <0 on error. * * Parameters: * modfile - A pointer to the raw MOD data * modlength - The length of the raw data in bytes * mod - A pointer to the MOD handle **/ int MODFILE_SetMOD(u8 *modfile, int modlength, MODFILE *mod) { int ofs = 0; int i; int sampledatalen; int retval = 0; if (modfile == NULL || mod == NULL) return -1; mod->nInstruments = 31; if ( (memcmp(&modfile[1080], "M.K.", 4) == 0) || (memcmp(&modfile[1080], "FLT4", 4) == 0) ) { mod->nChannels = 4; } else if (memcmp(&modfile[1080], "2CHN", 4) == 0) { mod->nChannels = 2; } else if (memcmp(&modfile[1080], "6CHN", 4) == 0) { mod->nChannels = 6; } else if (memcmp(&modfile[1080], "8CHN", 4) == 0) { mod->nChannels = 8; } else if (memcmp(&modfile[1080], "10CH", 4) == 0) { mod->nChannels = 10; } else if (memcmp(&modfile[1080], "12CH", 4) == 0) { mod->nChannels = 12; } else if (memcmp(&modfile[1080], "14CH", 4) == 0) { mod->nChannels = 14; } else if (memcmp(&modfile[1080], "16CH", 4) == 0) { mod->nChannels = 16; } else if (memcmp(&modfile[1080], "18CH", 4) == 0) { mod->nChannels = 18; } else if (memcmp(&modfile[1080], "20CH", 4) == 0) { mod->nChannels = 20; } else if (memcmp(&modfile[1080], "22CH", 4) == 0) { mod->nChannels = 22; } else if (memcmp(&modfile[1080], "24CH", 4) == 0) { mod->nChannels = 24; } else if (memcmp(&modfile[1080], "26CH", 4) == 0) { mod->nChannels = 26; } else if (memcmp(&modfile[1080], "28CH", 4) == 0) { mod->nChannels = 28; } else if (memcmp(&modfile[1080], "30CH", 4) == 0) { mod->nChannels = 30; } else if (memcmp(&modfile[1080], "32CH", 4) == 0) { mod->nChannels = 32; } else { mod->nInstruments = 15; mod->nChannels = 4; } mod->nChannels++; /* Global fx channel */ mod->nSamples = mod->nInstruments; /* The MOD format doesn't support multisamples */ /* 0 */ memcpy(mod->songname, &modfile[ofs], 20); ofs += 20; /* Instruments */ /* 20 */ /* mod->instruments = malloc(mod->nInstruments * sizeof(MOD_Instrument));*/ SAFE_MALLOC(mod->instruments, mod->nInstruments * sizeof(MOD_Instrument)); memset(mod->instruments, 0, mod->nInstruments * sizeof(MOD_Instrument)); /* mod->samples = malloc(mod->nSamples * sizeof(MOD_Sample));*/ SAFE_MALLOC(mod->samples, mod->nSamples * sizeof(MOD_Sample)); memset(mod->samples, 0, mod->nSamples * sizeof(MOD_Sample)); for (i = 0; i < mod->nInstruments; i++) { int temp, j; /* Name */ memcpy(mod->samples[i].name, &modfile[ofs], 22); ofs += 22; /* Length */ temp = modfile[ofs++] << 8; temp |= modfile[ofs++]; temp *= 2; mod->samples[i].sampleInfo.length = temp; /* Fine tune */ temp = modfile[ofs++]; if (temp > 7) temp -= 16; temp += 8; mod->samples[i].default_middle_c = s3m_finetunes[temp]; /* Volume */ mod->samples[i].default_volume = modfile[ofs++]; /* Loop start */ temp = modfile[ofs++] << 8; temp |= modfile[ofs++]; temp *= 2; mod->samples[i].sampleInfo.loop_start = temp; /* Loop end */ temp = modfile[ofs++] << 8; temp |= modfile[ofs++]; temp *= 2; mod->samples[i].sampleInfo.loop_end = mod->samples[i].sampleInfo.loop_start + temp; mod->samples[i].panning = 255; mod->samples[i].sampleInfo.bit_16 = FALSE; mod->samples[i].sampleInfo.stereo = FALSE; mod->samples[i].sampleInfo.pingpong = FALSE; mod->samples[i].relative_note = 0; mod->samples[i].sampleInfo.looped = TRUE; if (temp <= 2) { mod->samples[i].sampleInfo.looped = FALSE; mod->samples[i].sampleInfo.loop_start = mod->samples[i].sampleInfo.loop_end = mod->samples[i].sampleInfo.length - 1; } /* Define a new instrument */ strcpy(mod->instruments[i].name, mod->samples[i].name); for (j = 0; j < 256; j++) { mod->instruments[i].samples[j] = &mod->samples[i]; mod->instruments[i].note[j] = j; } /* Disable instrument envelopes */ mod->instruments[i].envPanning.enabled = FALSE; mod->instruments[i].envVolume.enabled = FALSE; mod->instruments[i].volumeFade = 32767; } /* Song length */ mod->songlength = modfile[ofs++]; /* CIAA speed */ ofs++; /* Arrangement */ memcpy(mod->playlist, &modfile[ofs], 128); ofs += 128; /* I.D. */ if (mod->nInstruments != 15) ofs += 4; /* Calculate number of patterns */ mod->nPatterns = calcNumOfPatterns(mod, modlength); /* for (i = mod->nPatterns = 0; i < mod->songlength; i++) { if (mod->playlist[i] > mod->nPatterns) mod->nPatterns = mod->playlist[i]; } mod->nPatterns++;*/ /* Extract the patterns */ /* mod->patterns = malloc(sizeof(MOD_Note*) * mod->nPatterns); mod->patternLengths = malloc(sizeof(int) * mod->nPatterns);*/ SAFE_MALLOC(mod->patterns, sizeof(MOD_Note*) * mod->nPatterns); SAFE_MALLOC(mod->patternLengths, sizeof(int) * mod->nPatterns); for (i = 0; i < mod->nPatterns; i++) { int pline, pchannel; u8 * curPattern; mod->patternLengths[i] = 64; /* Alloc mem for current pattern */ /* mod->patterns[i] = malloc(sizeof(MOD_Note) * mod->nChannels * 64);*/ SAFE_MALLOC(mod->patterns[i], sizeof(MOD_Note) * mod->nChannels * 64); memset(mod->patterns[i], 255, sizeof(MOD_Note) * mod->nChannels * 64); /* Convert MOD pattern to our format */ curPattern = &modfile[ofs]; for (pline = 0; pline < 64; pline++) { u8 *curLine = &curPattern[(mod->nChannels - 1) * 4 * pline]; MOD_Note *globalEffect = &mod->patterns[i][(pline * mod->nChannels) + mod->nChannels - 1]; for (pchannel = 0; pchannel < mod->nChannels - 1; pchannel++) { int j; u8 *curNote = &curLine[4 * pchannel]; u16 note; u8 dnote; u32 instrument; u16 effect; u8 operand; u8 volume; note = (curNote[0] & 0x0f) << 8; note |= curNote[1]; instrument = curNote[0] & 0xf0; instrument |= (curNote[2] & 0xf0) >> 4; effect = curNote[2] & 0x0f; operand = curNote[3]; volume = 255; /* Convert note */ dnote = 0xff; if (note != 0) { for (j = 0; j < NUM_AMIGA_FREQS; j++) { if (s3m_amiga2st3[j].amigafreq == note) dnote = s3m_amiga2st3[j].st3note; } } if ((dnote == 0xff) && (note != 0)) { /* Note not found */ fprintf(stderr, "Note not found: %x\n", note); retval = 1; } /* Convert effect */ switch (effect) { case 0x00: /* Arpeggio */ if (operand != 0) effect = EFFECT_ST00; else effect = EFFECT_NONE; break; case 0x01: /* Porta up */ effect = EFFECT_ST10; break; case 0x02: /* Porta down */ effect = EFFECT_ST20; break; case 0x03: /* Porta to note */ effect = EFFECT_ST30; break; case 0x04: /* Vibrato */ effect = EFFECT_ST40; break; case 0x05: /* Porta + volume slide */ effect = EFFECT_ST50; break; case 0x06: /* Vibrato + volume slide */ effect = EFFECT_ST60; break; case 0x07: /* Tremolo */ effect = EFFECT_ST70; break; case 0x09: /* Sample offset */ effect = EFFECT_ST90; break; case 0x0a: /* Volume slide */ effect = EFFECT_STa0; break; case 0x0b: /* Pattern jump */ globalEffect->effect[0] = EFFECT_STb0; globalEffect->operand[0] = operand; effect = EFFECT_NONE; operand = 0; break; case 0x0c: /* Set volume */ effect = EFFECT_NONE; if (operand > 64) operand = 64; volume = operand; break; case 0x0d: /* Pattern break */ operand = ((operand >> 4) * 10) + (operand & 0x0f); if (operand >= 64) operand = 0; if (globalEffect->effect[0] == EFFECT_STb0) { globalEffect->effect[0] = EFFECT_STg0; globalEffect->operand[1] = operand; } else { globalEffect->effect[0] = EFFECT_STd0; globalEffect->operand[0] = operand; } effect = EFFECT_NONE; operand = 0; break; case 0x0e: switch (operand >> 4) { int temp; case 0x01: /* Fine porta up */ effect = EFFECT_STe1; operand = operand & 0x0f; break; case 0x02: /* Fine porta down */ effect = EFFECT_STe2; operand = operand & 0x0f; break; case 0x04: /* Set vibrato waveform */ effect = EFFECT_STe4; operand = operand & 0x0f; break; case 0x05: /* Set finetune */ effect = EFFECT_STe5; temp = operand & 0x0f; if (temp > 7) temp -= 16; temp += 8; operand = operand & 0x0f; break; case 0x06: /* Pattern loop */ operand = operand & 0x0f; globalEffect->effect[0] = EFFECT_STe6; globalEffect->operand[0] = operand; effect = EFFECT_NONE; operand = 0; break; case 0x07: /* Set tremolo waveform */ effect = EFFECT_STe7; operand = operand & 0x0f; break; case 0x08: /* Panning */ effect = EFFECT_STe8; operand = operand & 0x0f; break; case 0x09: /* Retrig */ effect = EFFECT_STe9; operand = operand & 0x0f; break; case 0x0a: /* Fine volume slide up */ effect = EFFECT_STea; operand = operand & 0x0f; break; case 0x0b: /* Fine volume slide down */ effect = EFFECT_STeb; operand = operand & 0x0f; break; case 0x0c: /* Note cut */ effect = EFFECT_STec; operand = operand & 0x0f; break; case 0x0d: /* Note delay */ effect = EFFECT_STed; operand = operand & 0x0f; break; case 0x0e: /* Pattern delay */ operand = operand & 0x0f; globalEffect->effect[0] = EFFECT_STee; globalEffect->operand[0] = operand; effect = EFFECT_NONE; operand = 0; break; default: effect = EFFECT_NONE; break; } break; case 0x0f: /* Set speed/tempo */ /* if (operand < 32) effect = 'A' - 'A' + 1; else effect = 'T' - 'A' + 1;*/ effect = EFFECT_STf0; break; default: effect = EFFECT_NONE; break; } j = (pline * mod->nChannels) + pchannel; mod->patterns[i][j].note = dnote; mod->patterns[i][j].instrument = instrument; mod->patterns[i][j].volume = volume; mod->patterns[i][j].effect[0] = effect; mod->patterns[i][j].operand[0] = operand; } } ofs += 4 * (mod->nChannels - 1) * 64; } /* Sample data */ for (i = sampledatalen = 0; i < mod->nInstruments; i++) { sampledatalen += mod->samples[i].sampleInfo.length; } if ((sampledatalen + 1084 + (mod->nPatterns * 64 * (mod->nChannels - 1) * 4)) != modlength) { ofs = modlength - sampledatalen; } for (i = 0; i < mod->nInstruments; i++) { mod->samples[i].sampleInfo.sampledata = NULL; if (mod->samples[i].sampleInfo.length != 0) { /* mod->samples[i].sampleInfo.sampledata = malloc(mod->samples[i].sampleInfo.length);*/ SAFE_MALLOC(mod->samples[i].sampleInfo.sampledata, mod->samples[i].sampleInfo.length); memcpy(mod->samples[i].sampleInfo.sampledata, &modfile[ofs], mod->samples[i].sampleInfo.length); ofs += mod->samples[i].sampleInfo.length; } } for ( i = 0; i < mod->nChannels; i++) { mod->channels[i].voiceInfo.enabled = TRUE; mod->channels[i].default_panning = (((i - 1) >> 1) & 1) ^ 1 ? 86 : 167; } mod->start_speed = 6; mod->start_tempo = 125; mod->master_volume = 64; mod->filetype = MODULE_MOD; return retval; } /** * BOOL MODFILE_IsMOD(u8 *modfile, int modlength); * * Checks whether the raw data in memory is a valid * Protracker MOD file. * * Returns TRUE if the data is a Protracker MOD, * FALSE if not. * * Parameters: * * modfile - Pointer to the raw data to be checked * modlength - Length of the raw data in bytes **/ BOOL MODFILE_IsMOD(u8 *modfile, int modlength) { MODFILE temp; int ret; if ((modfile == NULL) || (modlength < 1080)) return FALSE; MODFILE_Init(&temp); if ((ret = MODFILE_SetMOD(modfile, modlength, &temp)) >= 0) { temp.set = TRUE; MODFILE_Free(&temp); } return ret == 0; } int MODFILE_MODGetFormatID(void) { return MODULE_MOD; } char *MODFILE_MODGetDescription(void) { return DESCRIPTION; } char *MODFILE_MODGetAuthor(void) { return AUTHOR; } char *MODFILE_MODGetVersion(void) { return VERSION; } char *MODFILE_MODGetCopyright(void) { return COPYRIGHT; }