#include <stdio.h>
#include "main.h"
#include "sonapatch.h"
// File format
enum {
// Algorithm/feedback
// (register $B0)
SPAT_ALGO_FEED,
// All slot data
// (registers $30 to $9C)
SPAT_S1_MUL_DT, SPAT_S3_MUL_DT, SPAT_S2_MUL_DT, SPAT_S4_MUL_DT,
SPAT_S1_TL, SPAT_S3_TL, SPAT_S2_TL, SPAT_S4_TL,
SPAT_S1_AR_RS, SPAT_S3_AR_RS, SPAT_S2_AR_RS, SPAT_S4_AR_RS,
SPAT_S1_DR_AMON, SPAT_S3_DR_AMON, SPAT_S2_DR_AMON, SPAT_S4_DR_AMON,
SPAT_S1_SR, SPAT_S3_SR, SPAT_S2_SR, SPAT_S4_SR,
SPAT_S1_RR_SL, SPAT_S3_RR_SL, SPAT_S2_RR_SL, SPAT_S4_RR_SL,
SPAT_S1_SSGEG, SPAT_S3_SSGEG, SPAT_S2_SSGEG, SPAT_S4_SSGEG,
// Unused yet, must be zero
SPAT_RESERVED_1,
SPAT_RESERVED_2,
SPAT_RESERVED_3,
// Size of the whole file
SIZE_SPAT
};
//***************************************************************************
// save_sonapatch
// Takes in the loaded FM patch and generates a SonaPatch file out of it.
//---------------------------------------------------------------------------
// param filename ... file name to save into
// param wave ....... pointer to patch data
//---------------------------------------------------------------------------
// return ........... 0 on success, -1 on failure
//***************************************************************************
int save_sonapatch(const char *filename, const Patch *patch)
{
// Where we store the data as it's being built
// We later write it all to the file in one go
uint8_t blob[SIZE_SPAT] = { 0 };
// YM2612 (and SonaPatch) want detune in a sign+magnitude format, but
// we get it in a plus-3 format instead, so we use this look-up table
// to convert from the latter to the former
static const uint8_t detune_table[] = { 7, 6, 5, 0, 1, 2, 3 };
// Generate blob out of the patch data
blob[SPAT_ALGO_FEED] = patch->algorithm |
patch->feedback << 3;
blob[SPAT_S1_MUL_DT] = patch->slot[0].multiplier |
detune_table[patch->slot[0].detune] << 4;
blob[SPAT_S3_MUL_DT] = patch->slot[1].multiplier |
detune_table[patch->slot[1].detune] << 4;
blob[SPAT_S2_MUL_DT] = patch->slot[2].multiplier |
detune_table[patch->slot[2].detune] << 4;
blob[SPAT_S4_MUL_DT] = patch->slot[3].multiplier |
detune_table[patch->slot[3].detune] << 4;
blob[SPAT_S1_TL] = patch->slot[0].total_level;
blob[SPAT_S3_TL] = patch->slot[1].total_level;
blob[SPAT_S2_TL] = patch->slot[2].total_level;
blob[SPAT_S4_TL] = patch->slot[3].total_level;
blob[SPAT_S1_AR_RS] = patch->slot[0].attack_rate |
patch->slot[0].rate_scaling << 6;
blob[SPAT_S3_AR_RS] = patch->slot[1].attack_rate |
patch->slot[1].rate_scaling << 6;
blob[SPAT_S2_AR_RS] = patch->slot[2].attack_rate |
patch->slot[2].rate_scaling << 6;
blob[SPAT_S4_AR_RS] = patch->slot[3].attack_rate |
patch->slot[3].rate_scaling << 6;
blob[SPAT_S1_DR_AMON] = patch->slot[0].decay_rate |
patch->slot[0].am_enable << 7;
blob[SPAT_S3_DR_AMON] = patch->slot[1].decay_rate |
patch->slot[1].am_enable << 7;
blob[SPAT_S2_DR_AMON] = patch->slot[2].decay_rate |
patch->slot[2].am_enable << 7;
blob[SPAT_S4_DR_AMON] = patch->slot[3].decay_rate |
patch->slot[3].am_enable << 7;
blob[SPAT_S1_SR] = patch->slot[0].sustain_rate;
blob[SPAT_S3_SR] = patch->slot[1].sustain_rate;
blob[SPAT_S2_SR] = patch->slot[2].sustain_rate;
blob[SPAT_S4_SR] = patch->slot[3].sustain_rate;
blob[SPAT_S1_RR_SL] = patch->slot[0].release_rate |
patch->slot[0].sustain_level << 4;
blob[SPAT_S3_RR_SL] = patch->slot[1].release_rate |
patch->slot[1].sustain_level << 4;
blob[SPAT_S2_RR_SL] = patch->slot[2].release_rate |
patch->slot[2].sustain_level << 4;
blob[SPAT_S4_RR_SL] = patch->slot[3].release_rate |
patch->slot[3].sustain_level << 4;
blob[SPAT_S1_SSGEG] = patch->slot[0].ssg_eg;
blob[SPAT_S3_SSGEG] = patch->slot[1].ssg_eg;
blob[SPAT_S2_SSGEG] = patch->slot[2].ssg_eg;
blob[SPAT_S4_SSGEG] = patch->slot[3].ssg_eg;
// Write the entire blob into the specified file
FILE *file = fopen(filename, "wb");
if (file == NULL) {
fprintf(stderr, "Error[\"%s\"]: can't create SonaPatch file\n",
filename);
return -1;
}
if (fwrite(&blob, 1, SIZE_SPAT, file) < SIZE_SPAT) {
fprintf(stderr, "Error[\"%s\"]: problem writing to SonaPatch file\n",
filename);
fclose(file);
return -1;
}
fclose(file);
return 0;
}