2020-09-06 12:18:54 -05:00
|
|
|
#include"mod.h"
|
|
|
|
|
|
|
|
MOD*mod_open(const char*fn)
|
|
|
|
{
|
|
|
|
FILE*file;
|
|
|
|
MOD*mod;
|
2020-09-06 18:51:55 -05:00
|
|
|
size_t highest_pattern;
|
2020-09-06 12:18:54 -05:00
|
|
|
|
|
|
|
// Open file
|
|
|
|
file=fopen(fn,"r");
|
|
|
|
if(!file)
|
|
|
|
{
|
|
|
|
printf("error: failed to open '%s'\n",fn);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allocate internal mod
|
|
|
|
mod=malloc(sizeof(MOD));
|
|
|
|
|
|
|
|
// Read file
|
2020-09-07 21:18:58 -05:00
|
|
|
fread(mod,154+NUMBER_OF_SAMPLES*sizeof(MODSAMPLE),1,file);
|
|
|
|
for(size_t i=0;i<NUMBER_OF_SAMPLES;++i)
|
2020-09-06 18:51:55 -05:00
|
|
|
mod->sample_data[i]=NULL; // Initialize pointers to NULL
|
|
|
|
|
|
|
|
// Get highest_pattern
|
2020-09-10 15:59:11 -05:00
|
|
|
highest_pattern=mod_gethighestpattern(mod);
|
2020-09-06 18:51:55 -05:00
|
|
|
|
|
|
|
// Allocate, read in pattern data
|
|
|
|
for(size_t i=0;i<highest_pattern+1;++i)
|
|
|
|
{
|
2020-09-08 07:17:11 -05:00
|
|
|
mod->patterns[i]=(MODPATTERN*)malloc(sizeof(MODPATTERN));
|
2020-09-06 18:51:55 -05:00
|
|
|
|
2020-09-06 20:52:45 -05:00
|
|
|
if(!mod->patterns[i])
|
2020-09-06 18:51:55 -05:00
|
|
|
{
|
|
|
|
printf("error: failed to allocate memory for pattern #%02x\n",
|
|
|
|
(uint32_t)i);
|
|
|
|
mod_delete(mod);
|
|
|
|
return NULL;
|
|
|
|
}
|
2020-09-08 08:40:16 -05:00
|
|
|
|
|
|
|
fread(mod->patterns[i],1,sizeof(MODPATTERN),file);
|
2020-09-06 18:51:55 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Allocate, read in sample data
|
|
|
|
for(size_t i=0;i<31;++i)
|
|
|
|
{
|
2020-10-10 08:29:00 -05:00
|
|
|
size_t samplelength=bswap_16(mod->samples[i].samplelength)*sizeof(int16_t);
|
2020-09-06 20:52:45 -05:00
|
|
|
|
2020-09-07 15:06:47 -05:00
|
|
|
if(samplelength>1)
|
2020-09-06 18:51:55 -05:00
|
|
|
{
|
|
|
|
// Allocate memory, verify
|
2020-09-06 20:52:45 -05:00
|
|
|
mod->sample_data[i]=(uint16_t*)malloc(samplelength);
|
2020-09-06 18:51:55 -05:00
|
|
|
|
|
|
|
if(!mod->sample_data[i])
|
|
|
|
{
|
|
|
|
printf("error: failed to allocate memory for sample #%02x (length: %lu)\n",
|
|
|
|
(uint32_t)i,
|
2020-09-06 20:52:45 -05:00
|
|
|
(size_t)samplelength);
|
2020-09-06 18:51:55 -05:00
|
|
|
mod_delete(mod);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read sample into new memory
|
2020-09-06 20:52:45 -05:00
|
|
|
fread(mod->sample_data[i],1,samplelength,file);
|
2020-09-06 18:51:55 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
mod->sample_data[i]=NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Done
|
2020-09-06 12:18:54 -05:00
|
|
|
fclose(file);
|
|
|
|
|
|
|
|
// Return
|
|
|
|
return mod;
|
|
|
|
}
|
|
|
|
|
2020-09-10 16:08:51 -05:00
|
|
|
void mod_print(MOD*mod)
|
2020-09-06 12:18:54 -05:00
|
|
|
{
|
|
|
|
int highest_pattern;
|
|
|
|
// PRINT DATA -----
|
|
|
|
// Overlay MOD mod struct, print data
|
|
|
|
printf("Mod name: '%.20s'\n",mod->name);
|
|
|
|
|
|
|
|
// Samples
|
|
|
|
printf("Sample\tName\t\t\t\tLength\tFine\tVolume\tStart\tRepeat\n");
|
|
|
|
for(int i=0,drawbar=1;i<NUMBER_OF_PATTERNS;++i)
|
|
|
|
{
|
2020-09-06 18:51:55 -05:00
|
|
|
// Skip samples that are 'empty' (samplelength == 0)
|
2020-09-07 15:06:47 -05:00
|
|
|
if(mod->samples[i].volume==0||mod->samples[i].samplelength<2)
|
2020-09-06 12:18:54 -05:00
|
|
|
{
|
|
|
|
drawbar&&puts("--------"),drawbar=0; // Draw one bar until next non-empty sample
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-09-06 18:51:55 -05:00
|
|
|
printf("%02x\t",i+1);
|
2020-09-06 12:18:54 -05:00
|
|
|
printf("'%22.22s'\t",mod->samples[i].name);
|
|
|
|
printf("% 6d\t",bswap_16(mod->samples[i].samplelength)*2);
|
|
|
|
printf("% 6d\t",bswap_16(mod->samples[i].finetune));
|
|
|
|
printf("% 6d\t",(mod->samples[i].volume));
|
|
|
|
printf("% 6d\t",bswap_16(mod->samples[i].repeatstart));
|
|
|
|
printf("% 6d",bswap_16(mod->samples[i].repeatlength));
|
|
|
|
puts("");
|
|
|
|
drawbar=1; // Draw a visual bar next 'empty' sample
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Number song positions: %d\n",mod->positions);
|
2020-09-07 21:18:58 -05:00
|
|
|
printf("MOD file type: '%.4s'\n",mod->magic);
|
2020-09-06 12:18:54 -05:00
|
|
|
|
|
|
|
// Pattern table
|
|
|
|
puts("Pattern Table:");
|
|
|
|
highest_pattern=mod->patterntable[0];
|
|
|
|
for(int i=0,inc=24;i<128&&i<mod->positions;i+=inc)
|
|
|
|
{
|
|
|
|
for(int j=0;j<inc&&(i+j<mod->positions);++j)
|
|
|
|
{
|
|
|
|
printf("%02x ",mod->patterntable[i+j]);
|
|
|
|
|
|
|
|
// Find highest pattern number (therefore the
|
|
|
|
// # of patterns that are stored)
|
|
|
|
if(mod->patterntable[i+j]>highest_pattern)
|
|
|
|
highest_pattern=mod->patterntable[i+j];
|
|
|
|
}
|
|
|
|
puts("");
|
|
|
|
}
|
2020-09-06 18:51:55 -05:00
|
|
|
printf("Number of patterns: %d (highest: %02x)\n",highest_pattern+1,highest_pattern);
|
2020-09-06 12:18:54 -05:00
|
|
|
|
2020-09-10 16:08:51 -05:00
|
|
|
// DONE PRINTING DATA -----
|
|
|
|
}
|
|
|
|
|
|
|
|
void mod_printpatterns(MOD*mod)
|
|
|
|
{
|
|
|
|
size_t highest_pattern=mod_gethighestpattern(mod);
|
2020-09-08 07:17:11 -05:00
|
|
|
// Print pattern/channel data
|
2020-09-10 16:08:51 -05:00
|
|
|
puts("\nPattern data:");
|
2020-10-10 08:29:00 -05:00
|
|
|
for(size_t i=0;i<=highest_pattern;++i)
|
2020-09-08 07:17:11 -05:00
|
|
|
{
|
2020-09-08 08:40:16 -05:00
|
|
|
|
2020-09-10 16:08:51 -05:00
|
|
|
// Display pattern number
|
|
|
|
printf("Pattern %02lx:\n",i);
|
|
|
|
for(size_t k=0;k<4;++k)
|
|
|
|
{
|
|
|
|
printf("fq sm fx");
|
|
|
|
if(k<3)
|
|
|
|
printf(" ");
|
|
|
|
}
|
|
|
|
puts("");
|
|
|
|
|
|
|
|
// Print channel data for each division j of pattern i
|
|
|
|
for(size_t j=0;j<4*64;)
|
2020-09-08 07:17:11 -05:00
|
|
|
{
|
2020-09-08 08:40:16 -05:00
|
|
|
|
2020-09-10 16:08:51 -05:00
|
|
|
// Print period, sample, and effect for this row for each channel
|
2020-09-08 08:40:16 -05:00
|
|
|
for(size_t k=0;k<4;++k)
|
2020-09-08 07:17:11 -05:00
|
|
|
{
|
2020-09-10 16:08:51 -05:00
|
|
|
uint32_t data=bswap_32(mod->patterns[i]->channel_data[j+k]);
|
|
|
|
|
|
|
|
//printf("%.3u %02x %03x",
|
|
|
|
printf("%s %02x %03x",
|
|
|
|
mod_getnotename((data&0x0fff0000)>>16),
|
|
|
|
//(data&0x0fff0000)>>16,
|
|
|
|
(((data&0xf0000000)>>12)>>12|(data&0xf000)>>12),
|
|
|
|
data&0xfff
|
|
|
|
//data
|
|
|
|
);
|
|
|
|
if(k<3)printf(" | ");
|
2020-09-08 08:40:16 -05:00
|
|
|
}
|
2020-09-10 16:08:51 -05:00
|
|
|
j+=4;
|
2020-09-08 08:40:16 -05:00
|
|
|
puts("");
|
2020-09-08 07:17:11 -05:00
|
|
|
}
|
|
|
|
}
|
2020-09-06 12:18:54 -05:00
|
|
|
}
|
|
|
|
|
2020-09-10 15:59:11 -05:00
|
|
|
size_t mod_gethighestpattern(MOD*mod)
|
|
|
|
{
|
|
|
|
size_t highest_pattern;
|
|
|
|
|
|
|
|
// Get highest_pattern
|
|
|
|
highest_pattern=mod->patterntable[0];
|
|
|
|
for(size_t i=0;i<mod->positions;++i)
|
|
|
|
if(highest_pattern<mod->patterntable[i])
|
|
|
|
highest_pattern=mod->patterntable[i];
|
|
|
|
return highest_pattern;
|
|
|
|
}
|
|
|
|
|
2020-09-06 18:51:55 -05:00
|
|
|
void mod_delete(MOD*mod)
|
|
|
|
{
|
|
|
|
size_t highest_pattern;
|
|
|
|
|
|
|
|
if(!mod)return;
|
|
|
|
|
|
|
|
// Get highest_pattern
|
|
|
|
highest_pattern=mod->patterntable[0];
|
|
|
|
for(size_t i=0;i<mod->positions;++i)
|
|
|
|
if(highest_pattern<mod->patterntable[i])
|
|
|
|
highest_pattern=mod->patterntable[i];
|
|
|
|
|
|
|
|
// Delete patterns
|
|
|
|
for(size_t i=0;i<highest_pattern+1;++i)
|
|
|
|
if(mod->patterns[i])
|
|
|
|
free(mod->patterns[i]);
|
|
|
|
|
|
|
|
// Delete samples
|
|
|
|
for(size_t i=0;i<31;++i)
|
|
|
|
if(mod->sample_data[i])
|
|
|
|
free(mod->sample_data[i]);
|
|
|
|
free(mod);
|
|
|
|
}
|
2020-09-10 15:59:11 -05:00
|
|
|
|
|
|
|
char*mod_getnotename(uint16_t note)
|
|
|
|
{
|
|
|
|
static uint16_t periods[]={
|
|
|
|
//C C# D D# E F F# G G# A A# B
|
|
|
|
1712,1616,1525,1440,1357,1281,1209,1141,1077,1017, 961, 907, //0
|
|
|
|
856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453, //1
|
|
|
|
428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, //2
|
|
|
|
214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113, //3
|
|
|
|
107, 101, 95, 90, 85, 80, 76, 71, 67, 64, 60, 57, //4
|
|
|
|
};
|
|
|
|
static char*names[]={"C","C#","D","D#","E","F","F#","G","G#","A","A#","B"};
|
|
|
|
static char name[16];
|
|
|
|
int which=-1;
|
|
|
|
|
|
|
|
if(note==0)return "---";
|
|
|
|
|
|
|
|
// Find which one
|
|
|
|
for(int i=0;i<sizeof(periods)/2;++i)
|
|
|
|
if(periods[i]==note)
|
|
|
|
which=i;
|
|
|
|
if(which<0)return "NA ";
|
|
|
|
|
|
|
|
// Return
|
|
|
|
int wrote;
|
|
|
|
wrote=sprintf(name,"%s%s%u",names[which%12],strlen(names[which%12])<2?"-":"",which/12);
|
|
|
|
if(wrote<3)strcat(name," ");
|
|
|
|
return name;
|
|
|
|
}
|