ref: ccfd02dd2dcfa72085c4dc4f29dea58313964b57
dir: /src/modloaders/ft2_load_bem.c/
/* BEM (UN05, MikMod) loader. Supports modules converted from XM only! ** ** Note: Data sanitation is done in the last stage ** of module loading, so you don't need to do that here. */ #include <stdio.h> #include <stdint.h> #include <stdbool.h> #include "../ft2_header.h" #include "../ft2_module_loader.h" #include "../ft2_sample_ed.h" #include "../ft2_sysreqs.h" #define MAX_TRACKS (256*32) #define FLAG_XMPERIODS 1 #define FLAG_LINEARSLIDES 2 #ifdef _MSC_VER // please don't mess with these structs! #pragma pack(push) #pragma pack(1) #endif typedef struct bemHdr_t { char id[4]; uint8_t numchn; uint16_t numpos; uint16_t reppos; uint16_t numpat; uint16_t numtrk; uint16_t numins; uint8_t initspeed; uint8_t inittempo; uint8_t positions[256]; uint8_t panning[32]; uint8_t flags; } #ifdef __GNUC__ __attribute__((packed)) #endif bemHdr_t; #ifdef _MSC_VER #pragma pack(pop) #endif enum { UNI_NOTE = 1, UNI_INSTRUMENT, UNI_PTEFFECT0, UNI_PTEFFECT1, UNI_PTEFFECT2, UNI_PTEFFECT3, UNI_PTEFFECT4, UNI_PTEFFECT5, UNI_PTEFFECT6, UNI_PTEFFECT7, UNI_PTEFFECT8, UNI_PTEFFECT9, UNI_PTEFFECTA, UNI_PTEFFECTB, UNI_PTEFFECTC, UNI_PTEFFECTD, UNI_PTEFFECTE, UNI_PTEFFECTF, UNI_S3MEFFECTA, UNI_S3MEFFECTD, UNI_S3MEFFECTE, UNI_S3MEFFECTF, UNI_S3MEFFECTI, UNI_S3MEFFECTQ, UNI_S3MEFFECTT, UNI_XMEFFECTA, UNI_XMEFFECTG, UNI_XMEFFECTH, UNI_XMEFFECTP, UNI_LAST }; static const uint8_t xmEfxTab[] = { 10, 16, 17, 25 }; // A, G, H, P static char *readString(FILE *f) { uint16_t length; fread(&length, 2, 1, f); char *out = (char *)malloc(length+1); if (out == NULL) return NULL; fread(out, 1, length, f); out[length] = '\0'; return out; } bool detectBEM(FILE *f) { if (f == NULL) return false; uint32_t oldPos = (uint32_t)ftell(f); fseek(f, 0, SEEK_SET); char ID[64]; memset(ID, 0, sizeof (ID)); fread(ID, 1, 4, f); if (memcmp(ID, "UN05", 4) != 0) goto error; fseek(f, 0x131, SEEK_SET); if (feof(f)) goto error; uint8_t flags = (uint8_t)fgetc(f); if ((flags & FLAG_XMPERIODS) == 0) goto error; fseek(f, 0x132, SEEK_SET); if (feof(f)) goto error; uint16_t strLength = 0; fread(&strLength, 2, 1, f); if (strLength == 0 || strLength > 512) goto error; fseek(f, strLength+2, SEEK_CUR); if (feof(f)) goto error; fread(ID, 1, 64, f); if (memcmp(ID, "FastTracker v2.00", 17) != 0) goto error; fseek(f, oldPos, SEEK_SET); return true; error: fseek(f, oldPos, SEEK_SET); return false; } bool loadBEM(FILE *f, uint32_t filesize) { bemHdr_t h; if (filesize < sizeof (h)) { loaderMsgBox("Error: This file is either not a module, or is not supported."); return false; } fread(&h, 1, sizeof (bemHdr_t), f); char *songName = readString(f); if (songName == NULL) return false; strcpy(songTmp.name, songName); free(songName); uint16_t strLength; fread(&strLength, 2, 1, f); fseek(f, strLength, SEEK_CUR); fread(&strLength, 2, 1, f); fseek(f, strLength, SEEK_CUR); if (h.numpos > 256 || h.numpat > 256 || h.numchn > 32 || h.numtrk > MAX_TRACKS) { loaderMsgBox("Error loading BEM: The module is corrupt!"); return false; } tmpLinearPeriodsFlag = !!(h.flags & FLAG_LINEARSLIDES); songTmp.numChannels = h.numchn; songTmp.songLength = h.numpos; songTmp.songLoopStart = h.reppos; songTmp.BPM = h.inittempo; songTmp.speed = h.initspeed; memcpy(songTmp.orders, h.positions, 256); // load instruments for (int16_t i = 0; i < h.numins; i++) { if (!allocateTmpInstr(1 + i)) { loaderMsgBox("Not enough memory!"); return false; } instr_t *ins = instrTmp[1 + i]; ins->numSamples = (uint8_t)fgetc(f); fread(ins->note2SampleLUT, 1, 96, f); ins->volEnvFlags = (uint8_t)fgetc(f); ins->volEnvLength = (uint8_t)fgetc(f); ins->volEnvSustain = (uint8_t)fgetc(f); ins->volEnvLoopStart = (uint8_t)fgetc(f); ins->volEnvLoopEnd = (uint8_t)fgetc(f); fread(ins->volEnvPoints, 2, 12*2, f); ins->panEnvFlags = (uint8_t)fgetc(f); ins->panEnvLength = (uint8_t)fgetc(f); ins->panEnvSustain = (uint8_t)fgetc(f); ins->panEnvLoopStart = (uint8_t)fgetc(f); ins->panEnvLoopEnd = (uint8_t)fgetc(f); fread(ins->panEnvPoints, 2, 12*2, f); ins->autoVibType = (uint8_t)fgetc(f); ins->autoVibSweep = (uint8_t)fgetc(f); ins->autoVibDepth = (uint8_t)fgetc(f); ins->autoVibRate = (uint8_t)fgetc(f); fread(&ins->fadeout, 2, 1, f); char *insName = readString(f); if (insName == NULL) return false; uint32_t insNameLen = (uint32_t)strlen(insName); if (insNameLen > 22) insNameLen = 22; memcpy(songTmp.instrName[1+i], insName, insNameLen); free(insName); for (int32_t j = 0; j < ins->numSamples; j++) { sample_t *s = &ins->smp[j]; s->finetune = (int8_t)fgetc(f) ^ 0x80; fseek(f, 1, SEEK_CUR); s->relativeNote = (int8_t)fgetc(f); s->volume = (uint8_t)fgetc(f); s->panning = (uint8_t)fgetc(f); fread(&s->length, 4, 1, f); fread(&s->loopStart, 4, 1, f); uint32_t loopEnd; fread(&loopEnd, 4, 1, f); s->loopLength = loopEnd - s->loopStart; uint16_t flags; fread(&flags, 2, 1, f); if (flags & 1) s->flags |= SAMPLE_16BIT; if (flags & 16) s->flags |= LOOP_FWD; if (flags & 32) s->flags |= LOOP_BIDI; char *smpName = readString(f); if (smpName == NULL) return false; uint32_t smpNameLen = (uint32_t)strlen(smpName); if (smpNameLen > 22) smpNameLen = 22; memcpy(s->name, smpName, smpNameLen); free(smpName); } } // load tracks uint16_t rowsInPattern[256]; uint16_t trackList[256*32]; fread(rowsInPattern, 2, h.numpat, f); fread(trackList, 2, h.numpat * h.numchn, f); note_t *decodedTrack[MAX_TRACKS]; for (int32_t i = 0; i < h.numtrk; i++) { uint16_t trackBytesInFile; fread(&trackBytesInFile, 2, 1, f); if (trackBytesInFile == 0) { loaderMsgBox("Error loading BEM: This module is corrupt!"); trackError: for (int32_t j = 0; j < i; j++) free(decodedTrack[j]); return false; } decodedTrack[i] = (note_t *)calloc(rowsInPattern[i], sizeof (note_t)); if (decodedTrack[i] == NULL) { loaderMsgBox("Not enough memory!"); goto trackError; } note_t *out = decodedTrack[i]; // decode track uint32_t trackPosInFile = (uint32_t)ftell(f); while ((uint32_t)ftell(f) < trackPosInFile+trackBytesInFile) { uint8_t byte = (uint8_t)fgetc(f); if (byte == 0) break; // end of track uint8_t repeat = byte >> 5; uint8_t opcodeBytes = (byte & 0x1F) - 1; uint32_t opcodeStart = (uint32_t)ftell(f); uint32_t opcodeEnd = opcodeStart + opcodeBytes; for (int32_t j = 0; j <= repeat; j++, out++) { fseek(f, opcodeStart, SEEK_SET); while ((uint32_t)ftell(f) < opcodeEnd) { uint8_t opcode = (uint8_t)fgetc(f); if (opcode == 0) break; if (opcode == UNI_NOTE) { out->note = 1 + (uint8_t)fgetc(f); } else if (opcode == UNI_INSTRUMENT) { out->instr = 1 + (uint8_t)fgetc(f); } else if (opcode >= UNI_PTEFFECT0 && opcode <= UNI_PTEFFECTF) // PT effects { out->efx = opcode - UNI_PTEFFECT0; out->efxData = (uint8_t)fgetc(f); } else if (opcode >= UNI_XMEFFECTA && opcode <= UNI_XMEFFECTP) // XM effects { out->efx = xmEfxTab[opcode-UNI_XMEFFECTA]; out->efxData = (uint8_t)fgetc(f); } else { if (opcode >= UNI_LAST) // illegal opcode { loaderMsgBox("Error loading BEM: illegal pattern opcode!"); goto trackError; } // unsupported opcode, skip it if (opcode > 0) fseek(f, 1, SEEK_CUR); } } } } } // create patterns from tracks for (int32_t i = 0; i < h.numpat; i++) { uint16_t numRows = rowsInPattern[i]; if (numRows == 0 || numRows > 256) continue; if (!allocateTmpPatt(i, numRows)) { loaderMsgBox("Not enough memory!"); return false; } note_t *dst = patternTmp[i]; for (int32_t j = 0; j < h.numchn; j++) { note_t *src = (note_t *)decodedTrack[trackList[(i * h.numchn) + j]]; if (src != NULL) { for (int32_t k = 0; k < numRows; k++) dst[(k * MAX_CHANNELS) + j] = src[k]; } } } // load sample data for (int32_t i = 0; i < h.numins; i++) { instr_t *ins = instrTmp[1 + i]; if (ins == NULL) continue; for (int32_t j = 0; j < ins->numSamples; j++) { sample_t *s = &ins->smp[j]; bool sampleIs16Bit = !!(s->flags & SAMPLE_16BIT); if (!allocateSmpData(s, s->length, sampleIs16Bit)) { loaderMsgBox("Not enough memory!"); return false; } fread(s->dataPtr, 1 + sampleIs16Bit, s->length, f); delta2Samp(s->dataPtr, s->length, s->flags); } } return true; }