400 lines
13 KiB
C
400 lines
13 KiB
C
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "file.h"
|
|
|
|
size_t filesize;
|
|
|
|
Song* File_loadSong(const char *path)
|
|
{
|
|
Song *synSong;
|
|
FILE *f;
|
|
uint8_t *buffer;
|
|
size_t size;
|
|
|
|
if (!(f = fopen(path, "rb"))) return NULL;
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
size = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
if (!(buffer = (uint8_t *) malloc(size))) {
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
|
|
if (fread(buffer, 1, size, f) != size) {
|
|
free(buffer);
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
synSong = File_loadSongMem(buffer, size);
|
|
|
|
free(buffer);
|
|
|
|
return synSong;
|
|
}
|
|
|
|
uint16_t get_le16(const void *p)
|
|
{
|
|
return (((const uint8_t*)p)[0]) +
|
|
(((const uint8_t*)p)[1]) * 0x100;
|
|
}
|
|
|
|
uint32_t get_le32(const void *p)
|
|
{
|
|
return (((const uint8_t*)p)[0]) +
|
|
(((const uint8_t*)p)[1]) * 0x100 +
|
|
(((const uint8_t*)p)[2]) * 0x10000 +
|
|
(((const uint8_t*)p)[3]) * 0x1000000;
|
|
}
|
|
|
|
static long File_readHeader(SongHeader *h, const uint8_t *buffer, size_t size)
|
|
{
|
|
if (size < 52) return -1;
|
|
|
|
h->version = get_le16(buffer);
|
|
h->UNK00 = get_le16(buffer + 2);
|
|
h->patNum = get_le32(buffer + 4);
|
|
h->subsongNum = get_le32(buffer + 8);
|
|
h->instrNum = get_le32(buffer + 12);
|
|
h->UNK01 = get_le32(buffer + 16);
|
|
h->UNK02 = get_le16(buffer + 20);
|
|
h->UNK03 = get_le16(buffer + 22);
|
|
h->UNK04 = get_le16(buffer + 24);
|
|
h->UNK05 = get_le16(buffer + 26);
|
|
h->UNK06 = get_le16(buffer + 28);
|
|
h->UNK07 = get_le16(buffer + 30);
|
|
h->UNK08 = get_le16(buffer + 32);
|
|
h->UNK09 = get_le16(buffer + 34);
|
|
h->UNK0A = get_le16(buffer + 36);
|
|
h->UNK0B = get_le16(buffer + 38);
|
|
h->UNK0C = get_le16(buffer + 40);
|
|
h->UNK0D = get_le16(buffer + 42);
|
|
h->UNK0E = get_le16(buffer + 44);
|
|
h->UNK0F = get_le16(buffer + 46);
|
|
h->UNK10 = get_le16(buffer + 48);
|
|
h->UNK11 = get_le16(buffer + 50);
|
|
|
|
return 52;
|
|
}
|
|
|
|
static long File_readSubSong(Subsong *subSong, const uint8_t *buffer, size_t size)
|
|
{
|
|
int i, j;
|
|
|
|
if (size < 16564) return -1;
|
|
|
|
for (i = 0; i < 16; i++)
|
|
subSong->UNK00[i] = get_le32(buffer + i * 4);
|
|
|
|
for (i = 0; i < SE_MAXCHANS; i++)
|
|
subSong->mutedChans[i] = buffer[64 + i];
|
|
|
|
subSong->tempo = get_le32(buffer + 80);
|
|
subSong->groove = get_le32(buffer + 84);
|
|
subSong->startPosCoarse = get_le32(buffer + 88);
|
|
subSong->startPosFine = get_le32(buffer + 92);
|
|
subSong->endPosCoarse = get_le32(buffer + 96);
|
|
subSong->endPosFine = get_le32(buffer + 100);
|
|
subSong->loopPosCoarse = get_le32(buffer + 104);
|
|
subSong->loopPosFine = get_le32(buffer + 108);
|
|
subSong->isLooping = get_le16(buffer + 112);
|
|
|
|
memcpy(subSong->m_Name, buffer + 114, 32);
|
|
subSong->m_Name[32] = '\0';
|
|
|
|
subSong->channelNumber = get_le16(buffer + 146);
|
|
subSong->delayTime = get_le16(buffer + 148);
|
|
|
|
for (i = 0; i < SE_MAXCHANS; i++)
|
|
subSong->chanDelayAmt[i] = buffer[150 + i];
|
|
|
|
subSong->amplification = get_le16(buffer + 166);
|
|
|
|
subSong->UNK01 = get_le16(buffer + 168);
|
|
subSong->UNK02 = get_le16(buffer + 170);
|
|
subSong->UNK03 = get_le16(buffer + 172);
|
|
subSong->UNK04 = get_le16(buffer + 174);
|
|
subSong->UNK05 = get_le16(buffer + 176);
|
|
subSong->UNK06 = get_le16(buffer + 178);
|
|
|
|
for (i = 0; i < SE_MAXCHANS; i++) {
|
|
for (j = 0; j < 0x100; j++) {
|
|
subSong->orders[i][j].patIndex = get_le16(buffer + 180 + i * 1024 + j * 4);
|
|
subSong->orders[i][j].patLen = get_le16(buffer + 180 + i * 1024 + j * 4 + 2);
|
|
}
|
|
}
|
|
|
|
return 16564;
|
|
}
|
|
|
|
long File_readRow(Row *r, const uint8_t *buffer, size_t size)
|
|
{
|
|
if (size < 5) return -1;
|
|
|
|
r->note = buffer[0];
|
|
r->dest = buffer[1];
|
|
r->instr = buffer[2];
|
|
r->spd = buffer[3];
|
|
r->command = buffer[4];
|
|
|
|
return 5;
|
|
}
|
|
|
|
static long File_readInstrumentEffect(InstrumentEffect *effect, const uint8_t *buffer, size_t size)
|
|
{
|
|
if (size < 40) return -1;
|
|
|
|
effect->destWave = get_le32(buffer);
|
|
effect->srcWave1 = get_le32(buffer + 4);
|
|
effect->srcWave2 = get_le32(buffer + 8);
|
|
effect->oscWave = get_le32(buffer + 12);
|
|
effect->variable1 = get_le32(buffer + 16);
|
|
effect->variable2 = get_le32(buffer + 20);
|
|
effect->fxSpeed = get_le32(buffer + 24);
|
|
effect->oscSpeed = get_le32(buffer + 28);
|
|
effect->effectType = get_le32(buffer + 32);
|
|
effect->oscSelect = buffer[36];
|
|
effect->resetEffect = buffer[37];
|
|
effect->UNK00 = get_le16(buffer + 38);
|
|
|
|
return 40;
|
|
}
|
|
|
|
static long File_readInstrument(Instrument *instr, const uint8_t *buffer, size_t size)
|
|
{
|
|
int i, j;
|
|
long sizeRead;
|
|
|
|
if (size < 8712) return -1;
|
|
|
|
instr->version = get_le16(buffer);
|
|
|
|
memcpy(instr->name, buffer + 2, 32);
|
|
instr->name[32] = '\0';
|
|
|
|
instr->waveform = get_le16(buffer + 34);
|
|
instr->wavelength = get_le16(buffer + 36);
|
|
instr->masterVolume = get_le16(buffer + 38);
|
|
instr->amWave = get_le16(buffer + 40);
|
|
instr->amSpeed = get_le16(buffer + 42);
|
|
instr->amLoopPoint = get_le16(buffer + 44);
|
|
instr->finetune = get_le16(buffer + 46);
|
|
instr->fmWave = get_le16(buffer + 48);
|
|
instr->fmSpeed = get_le16(buffer + 50);
|
|
instr->fmLoopPoint = get_le16(buffer + 52);
|
|
instr->fmDelay = get_le16(buffer + 54);
|
|
instr->arpIndex = get_le16(buffer + 56);
|
|
|
|
for (i = 0; i < SE_MAXCHANS; i++)
|
|
instr->m_ResetWave[i] = buffer[58 + i];
|
|
|
|
instr->panWave = get_le16(buffer + 74);
|
|
instr->panSpeed = get_le16(buffer + 76);
|
|
instr->panLoopPoint = get_le16(buffer + 78);
|
|
instr->UNK00 = get_le16(buffer + 80);
|
|
instr->UNK01 = get_le16(buffer + 82);
|
|
instr->UNK02 = get_le16(buffer + 84);
|
|
instr->UNK03 = get_le16(buffer + 86);
|
|
instr->UNK04 = get_le16(buffer + 88);
|
|
instr->UNK05 = get_le16(buffer + 90);
|
|
|
|
buffer += 92; size -= 92;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
sizeRead = File_readInstrumentEffect(&instr->effects[i], buffer, size);
|
|
if (sizeRead < 0) return -1;
|
|
buffer += sizeRead; size -= sizeRead;
|
|
}
|
|
|
|
memcpy(instr->smpFullImportPath, buffer, 192);
|
|
instr->smpFullImportPath[192] = '\0';
|
|
|
|
buffer += 192; size -= 192;
|
|
|
|
instr->UNK06 = get_le32(buffer);
|
|
instr->UNK07 = get_le32(buffer + 4);
|
|
instr->UNK08 = get_le32(buffer + 8);
|
|
instr->UNK09 = get_le32(buffer + 12);
|
|
instr->UNK0A = get_le32(buffer + 16);
|
|
instr->UNK0B = get_le32(buffer + 20);
|
|
instr->UNK0C = get_le32(buffer + 24);
|
|
instr->UNK0D = get_le32(buffer + 28);
|
|
instr->UNK0E = get_le32(buffer + 32);
|
|
instr->UNK0F = get_le32(buffer + 36);
|
|
instr->UNK10 = get_le32(buffer + 40);
|
|
instr->UNK11 = get_le32(buffer + 44);
|
|
instr->UNK12 = get_le16(buffer + 48);
|
|
|
|
buffer += 50; size -= 50;
|
|
|
|
instr->shareSmpDataFromInstr = get_le16(buffer);
|
|
instr->hasLoop = get_le16(buffer + 2);
|
|
instr->hasBidiLoop = get_le16(buffer + 4);
|
|
|
|
buffer += 6; size -= 6;
|
|
|
|
instr->smpStartPoint = get_le32(buffer);
|
|
instr->smpLoopPoint = get_le32(buffer + 4);
|
|
instr->smpEndPoint = get_le32(buffer + 8);
|
|
instr->hasSample = get_le32(buffer + 12);
|
|
instr->smpLength = get_le32(buffer + 16);
|
|
|
|
buffer += 20; size -= 20;
|
|
|
|
for (i = 0; i < SE_MAXCHANS; i++) {
|
|
for (j = 0; j < 0x100; j++) {
|
|
instr->synthBuffers[i][j] = get_le16(buffer + i * 512 + j * 2);
|
|
}
|
|
}
|
|
|
|
return 8712;
|
|
}
|
|
|
|
Song* File_loadSongMem(const uint8_t *buffer, size_t size)
|
|
{
|
|
int i, j/*, k*/;
|
|
int songVer;
|
|
/*Subsong *subs;
|
|
Order *orderCol;
|
|
Order *order;
|
|
Row *row;*/
|
|
Instrument *instr;
|
|
Song *synSong;
|
|
long sizeRead;
|
|
|
|
synSong = (Song *) calloc(1, sizeof(Song));
|
|
if (!synSong) return NULL;
|
|
|
|
/*
|
|
//unused vars
|
|
int8_t _local5[] = [0, 0, 0, 0, 0, 0];
|
|
bool _local2 = false;
|
|
int _local7 = 0;
|
|
bool _local8 = true;
|
|
*/
|
|
|
|
sizeRead = File_readHeader(&synSong->h, buffer, size);
|
|
if (sizeRead < 0) goto FAIL;
|
|
buffer += sizeRead; size -= sizeRead;
|
|
|
|
songVer = synSong->h.version;
|
|
if ((songVer >= 3456) && (songVer <= 3457)){
|
|
if (synSong->h.subsongNum > 0){
|
|
synSong->subsongs = (Subsong *) malloc(synSong->h.subsongNum *sizeof(Subsong));
|
|
if (!synSong->subsongs) goto FAIL;
|
|
|
|
for (i = 0; i < synSong->h.subsongNum; i++) {
|
|
sizeRead = File_readSubSong(synSong->subsongs + i, buffer, size);
|
|
if (sizeRead < 0) goto FAIL;
|
|
buffer += sizeRead; size -= sizeRead;
|
|
}
|
|
|
|
synSong->rows = (Row *) malloc(synSong->h.patNum * 64 *sizeof(Row));
|
|
if (!synSong->rows) goto FAIL;
|
|
|
|
for (i = 0, j = synSong->h.patNum * 64; i < j; i++) {
|
|
sizeRead = File_readRow(synSong->rows + i, buffer, size);
|
|
if (sizeRead < 0) goto FAIL;
|
|
buffer += sizeRead; size -= sizeRead;
|
|
}
|
|
|
|
synSong->patNameSizes = (uint32_t *) malloc(synSong->h.patNum * sizeof(uint32_t));
|
|
if (!synSong->patNameSizes) goto FAIL;
|
|
synSong->patternNames = calloc(sizeof(char *), synSong->h.patNum);
|
|
if (!synSong->patternNames) goto FAIL;
|
|
|
|
for (i = 0; i < synSong->h.patNum; i++) {
|
|
if (size < 4) goto FAIL;
|
|
j = synSong->patNameSizes[i] = get_le32(buffer);
|
|
buffer += 4; size -= 4;
|
|
|
|
if (size < j) goto FAIL;
|
|
|
|
synSong->patternNames[i] = malloc(j + sizeof(char));
|
|
if (!synSong->patternNames[i]) goto FAIL;
|
|
|
|
memcpy(synSong->patternNames[i], buffer, j);
|
|
synSong->patternNames[i][j] = '\0';
|
|
|
|
buffer += j; size -= j;
|
|
}
|
|
|
|
synSong->instruments = malloc(synSong->h.instrNum * sizeof(Instrument));
|
|
if (!synSong->instruments) goto FAIL;
|
|
synSong->samples = calloc(sizeof(int16_t *), synSong->h.instrNum);
|
|
if (!synSong->samples) goto FAIL;
|
|
|
|
for (i = 0; i < synSong->h.instrNum; i++) {
|
|
instr = &synSong->instruments[i];
|
|
sizeRead = File_readInstrument(instr, buffer, size);
|
|
if (sizeRead < 0) goto FAIL;
|
|
buffer += sizeRead; size -= sizeRead;
|
|
|
|
if (songVer == 3456){
|
|
instr->shareSmpDataFromInstr = 0;
|
|
instr->hasLoop = 0;
|
|
instr->hasBidiLoop = 0;
|
|
instr->smpStartPoint = 0;
|
|
instr->smpLoopPoint = 0;
|
|
instr->smpEndPoint = 0;
|
|
if (instr->hasSample){
|
|
instr->smpStartPoint = 0;
|
|
instr->smpEndPoint = (instr->smpLength / 2);
|
|
instr->smpLoopPoint = 0;
|
|
}
|
|
}
|
|
if (instr->hasSample){
|
|
//instr->smpLength is in bytes, I think
|
|
if (size < instr->smpLength) goto FAIL;
|
|
synSong->samples[i] = malloc(instr->smpLength);
|
|
if (!synSong->samples[i]) goto FAIL;
|
|
memcpy(synSong->samples[i], buffer, instr->smpLength);
|
|
buffer += instr->smpLength; size -= instr->smpLength;
|
|
} else {
|
|
synSong->samples[i] = NULL;
|
|
}
|
|
|
|
}
|
|
memcpy(synSong->arpTable, buffer, 0x100);
|
|
buffer += 0x100; size -= 0x100;
|
|
} else goto FAIL;
|
|
} else goto FAIL;
|
|
|
|
return synSong;
|
|
|
|
FAIL:
|
|
File_freeSong(synSong);
|
|
return NULL;
|
|
}
|
|
|
|
void File_freeSong(Song *synSong)
|
|
{
|
|
int i;
|
|
|
|
if (synSong) {
|
|
if (synSong->subsongs) free(synSong->subsongs);
|
|
if (synSong->rows) free(synSong->rows);
|
|
if (synSong->patNameSizes) free(synSong->patNameSizes);
|
|
if (synSong->patternNames) {
|
|
for (i = 0; i < synSong->h.patNum; i++) {
|
|
if (synSong->patternNames[i]) free(synSong->patternNames[i]);
|
|
}
|
|
free(synSong->patternNames);
|
|
}
|
|
if (synSong->instruments) free(synSong->instruments);
|
|
if (synSong->samples) {
|
|
for (i = 0; i < synSong->h.instrNum; i++) {
|
|
if (synSong->samples[i]) free(synSong->samples[i]);
|
|
}
|
|
free(synSong->samples);
|
|
}
|
|
free(synSong);
|
|
}
|
|
}
|