#include <stdio.h>
#include <stdlib.h>
#include "main.h"
#include "tfi.h"
#define TFI_SIZE 42 // Size of a TFI file
#define MAX_BLOBSIZE (TFI_SIZE+1) // How much to allocate to blob
// Macro used to scan through the blob
// It also checks that the value is between 0 and max
// If that isn't the case then it jumps to format_error
#define GET_BYTE(var, max) \
temp = *ptr++; \
if (temp > (max)) goto format_error; \
var = temp;
//***************************************************************************
// load_tfi
// Parses and loads a TFI file into memory.
//---------------------------------------------------------------------------
// param filename ... file name to load from
//---------------------------------------------------------------------------
// return ........... pointer to FM patch (NULL on failure)
//***************************************************************************
Patch *load_tfi(const char *filename)
{
// Read the entire TFI file into memory
// We try to read a bit more ahead, this way we can tell for sure
// if the filesize is correct (TFI files are *exactly* 42 bytes)
uint8_t blob[MAX_BLOBSIZE];
FILE *file = fopen(filename, "rb");
if (file == NULL) {
fprintf(stderr, "Error[\"%s\"]: can't open TFI file\n", filename);
return NULL;
}
if (fread(&blob, 1, MAX_BLOBSIZE, file) != TFI_SIZE) {
fprintf(stderr, feof(file) ?
"Error[\"%s\"]: not a valid TFI file\n" :
"Error[\"%s\"]: problem reading TFI file\n",
filename);
return NULL;
}
fclose(file);
// Allocate memory to store the patch data
Patch *patch = malloc(sizeof(Patch));
if (patch == NULL) {
fprintf(stderr, "Error[\"%s\"]: out of memory!\n", filename);
return NULL;
}
// Now we read through the blob in memory
// These two variables are used by the GET_BYTE macro
const uint8_t *ptr = blob; // Pointer to next byte
uint8_t temp; // Used for the comparison
// Read common arguments
GET_BYTE(patch->algorithm, 7);
GET_BYTE(patch->feedback, 7);
// Read all slots
for (int i = 0; i < 4; i++) {
GET_BYTE(patch->slot[i].multiplier, 15);
GET_BYTE(patch->slot[i].detune, 6);
GET_BYTE(patch->slot[i].total_level, 127);
GET_BYTE(patch->slot[i].rate_scaling, 3);
GET_BYTE(patch->slot[i].attack_rate, 31);
GET_BYTE(patch->slot[i].decay_rate, 31);
GET_BYTE(patch->slot[i].sustain_rate, 31);
GET_BYTE(patch->slot[i].release_rate, 15);
GET_BYTE(patch->slot[i].sustain_level, 15);
GET_BYTE(patch->slot[i].ssg_eg, 15);
}
// TFI doesn't have AMS information, so fake it by setting the AMON bit
// on output slots (which will make AMS act as tremolo instead of altering
// the resulting sound)
patch->slot[0].am_enable = (patch->algorithm == 7) ? 1 : 0;
patch->slot[1].am_enable = (patch->algorithm >= 4) ? 1 : 0;
patch->slot[2].am_enable = (patch->algorithm >= 5) ? 1 : 0;
patch->slot[3].am_enable = 1;
// We're done
return patch;
format_error:
// If we get here, it means the TFI file is not valid
free(patch);
fprintf(stderr, "Error[\"%s\"]: not a valid TFI file\n", filename);
return NULL;
}