1345 lines
31 KiB
C++
1345 lines
31 KiB
C++
/**
|
|
* @file tracker.cc
|
|
*
|
|
* @brief Legacy code from Hyrule Magic TrackerLogic.c
|
|
*
|
|
* @details Attemtping to extract the song banks and convert them into SPC
|
|
* format for the snes_spc library.
|
|
*
|
|
*/
|
|
#include "tracker.h"
|
|
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "absl/status/status.h"
|
|
#include "app/core/constants.h"
|
|
#include "app/gfx/bitmap.h"
|
|
#include "app/gfx/snes_tile.h"
|
|
#include "app/rom.h"
|
|
#include "snes_spc/snes_spc/spc.h"
|
|
|
|
namespace yaze {
|
|
namespace app {
|
|
namespace zelda3 {
|
|
|
|
namespace {
|
|
|
|
void AddSPCReloc(SongSPCBlock *sbl, short addr) {
|
|
sbl->relocs[sbl->relnum++] = addr;
|
|
if (sbl->relnum == sbl->relsz) {
|
|
sbl->relsz += 16;
|
|
sbl->relocs = (unsigned short *)realloc(sbl->relocs, sbl->relsz << 1);
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// =============================================================================
|
|
|
|
SongSPCBlock *Tracker::AllocSPCBlock(int len, int bank) {
|
|
SongSPCBlock *sbl;
|
|
if (!len) {
|
|
printf("warning zero length block allocated");
|
|
}
|
|
if (ss_num == ss_size) {
|
|
ss_size += 512;
|
|
ssblt = (SongSPCBlock **)realloc(ssblt, ss_size << 2);
|
|
}
|
|
ssblt[ss_num] = sbl = (SongSPCBlock *)malloc(sizeof(SongSPCBlock));
|
|
ss_num++;
|
|
sbl->start = ss_next;
|
|
sbl->len = len;
|
|
sbl->buf = (uchar *)malloc(len);
|
|
sbl->relocs = (ushort *)malloc(32);
|
|
sbl->relsz = 16;
|
|
sbl->relnum = 0;
|
|
sbl->bank = bank & 7;
|
|
sbl->flag = bank >> 3;
|
|
ss_next += len;
|
|
return sbl;
|
|
}
|
|
|
|
// =============================================================================
|
|
|
|
unsigned char *Tracker::GetSPCAddr(ROM &rom, unsigned short addr, short bank) {
|
|
unsigned char *rom_ptr;
|
|
unsigned short a;
|
|
unsigned short b;
|
|
spcbank = bank + 1;
|
|
|
|
again:
|
|
rom_ptr = rom.data() + sbank_ofs[spcbank];
|
|
|
|
for (;;) {
|
|
a = *(unsigned short *)rom_ptr;
|
|
|
|
if (!a) {
|
|
if (spcbank) {
|
|
spcbank = 0;
|
|
|
|
goto again;
|
|
} else
|
|
return nullptr;
|
|
}
|
|
|
|
b = *(unsigned short *)(rom_ptr + 2);
|
|
rom_ptr += 4;
|
|
|
|
if (addr >= b && addr - b < a) {
|
|
spclen = a;
|
|
|
|
return rom_ptr + addr - b;
|
|
}
|
|
|
|
rom_ptr += a;
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
|
|
short Tracker::AllocSPCCommand() {
|
|
int i = m_free;
|
|
int j;
|
|
int k;
|
|
SPCCommand *spc_command;
|
|
if (i == -1) {
|
|
j = m_size;
|
|
m_size += 1024;
|
|
spc_command = current_spc_command_ = (SPCCommand *)realloc(
|
|
current_spc_command_, m_size * sizeof(SPCCommand));
|
|
k = 1023;
|
|
while (k--) spc_command[j].next = j + 1, j++;
|
|
spc_command[j].next = -1;
|
|
k = 1023;
|
|
while (k--) spc_command[j].prev = j - 1, j--;
|
|
spc_command[j].prev = -1;
|
|
i = j;
|
|
} else
|
|
spc_command = current_spc_command_;
|
|
m_free = spc_command[m_free].next;
|
|
if (m_free != -1) spc_command[m_free].prev = -1;
|
|
return i;
|
|
}
|
|
|
|
// =============================================================================
|
|
|
|
short Tracker::GetBlockTime(ROM &rom, short num, short prevtime) {
|
|
SPCCommand *spc_command = current_spc_command_;
|
|
SPCCommand *spc_command2;
|
|
|
|
int i = -1;
|
|
int j = 0;
|
|
int k = 0;
|
|
int l;
|
|
int m = 0;
|
|
int n = prevtime;
|
|
l = num;
|
|
|
|
if (l == -1) return 0;
|
|
|
|
for (;;) {
|
|
if (spc_command[l].flag & 4) {
|
|
j = spc_command[l].tim;
|
|
m = spc_command[l].tim2;
|
|
k = 1;
|
|
}
|
|
|
|
if (!k) i = l;
|
|
|
|
if (spc_command[l].flag & 1) n = spc_command[l].b1;
|
|
|
|
l = spc_command[l].next;
|
|
|
|
if (l == -1) {
|
|
if (!k) {
|
|
m = 0;
|
|
j = 0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i != -1)
|
|
for (;;) {
|
|
if (i == -1) {
|
|
printf("Error");
|
|
m_modf = 1;
|
|
return 0;
|
|
}
|
|
spc_command2 = spc_command + i;
|
|
if (spc_command2->cmd == 0xef) {
|
|
k = *(short *)&(spc_command2->p1);
|
|
if (k >= m_size) {
|
|
printf("Invalid music address\n");
|
|
m_modf = 1;
|
|
return 0;
|
|
}
|
|
if (spc_command2->flag & 1) {
|
|
j += GetBlockTime(rom, k, 0) * spc_command2->p3;
|
|
if (ss_lasttime) {
|
|
j += ss_lasttime * m;
|
|
j += spc_command[k].tim2 * spc_command2->b1;
|
|
j += (spc_command2->p3 - 1) * spc_command[k].tim2 * ss_lasttime;
|
|
} else {
|
|
j +=
|
|
spc_command2->b1 * (m + spc_command[k].tim2 * spc_command2->p3);
|
|
}
|
|
m = 0;
|
|
} else {
|
|
j += GetBlockTime(rom, k, 0) * spc_command2->p3;
|
|
j += ss_lasttime * m;
|
|
if (ss_lasttime)
|
|
j += (spc_command2->p3 - 1) * ss_lasttime * spc_command[k].tim2,
|
|
m = spc_command[k].tim2;
|
|
else
|
|
m += spc_command[k].tim2 * spc_command2->p3;
|
|
}
|
|
} else {
|
|
if (spc_command2->cmd < 0xe0) m++;
|
|
if (spc_command2->flag & 1) {
|
|
j += m * spc_command[i].b1;
|
|
m = 0;
|
|
}
|
|
}
|
|
spc_command2->tim = j;
|
|
spc_command2->tim2 = m;
|
|
spc_command2->flag |= 4;
|
|
|
|
if (i == num) break;
|
|
|
|
i = spc_command2->prev;
|
|
}
|
|
|
|
ss_lasttime = n;
|
|
return spc_command[num].tim + prevtime * spc_command[num].tim2;
|
|
}
|
|
|
|
// =============================================================================
|
|
|
|
short Tracker::LoadSPCCommand(ROM &rom, unsigned short addr, short bank,
|
|
int t) {
|
|
int b = 0;
|
|
int c = 0;
|
|
int d = 0;
|
|
int e = 0;
|
|
int f = 0;
|
|
int g = 0;
|
|
int h = 0;
|
|
int i = 0;
|
|
int l = 0;
|
|
int m = 0;
|
|
int n = 0;
|
|
int o = 0;
|
|
|
|
unsigned char j = 0;
|
|
unsigned char k = 0;
|
|
|
|
unsigned char *a = nullptr;
|
|
|
|
SongRange *sr;
|
|
SPCCommand *spc_command = current_spc_command_;
|
|
SPCCommand *spc_command2;
|
|
if (!addr) return -1;
|
|
|
|
a = GetSPCAddr(rom, addr, bank);
|
|
d = spcbank;
|
|
if (!a) {
|
|
printf("Address not found when loading track");
|
|
return -1;
|
|
}
|
|
sr = song_range_;
|
|
e = srnum;
|
|
f = 0x10000;
|
|
|
|
for (c = 0; c < e; c++) {
|
|
if (sr[c].bank == d)
|
|
if (sr[c].start > addr) {
|
|
if (sr[c].start < f) f = sr[c].start;
|
|
n = c;
|
|
} else if (sr[c].end > addr) {
|
|
for (f = sr[c].first; f != -1; f = spc_command[f].next) {
|
|
if (spc_command[f].flag & 4)
|
|
m = spc_command[f].tim, o = spc_command[f].tim2;
|
|
if (spc_command[f].addr == addr) {
|
|
spc_command[f].tim = m;
|
|
spc_command[f].tim2 = o;
|
|
lastsr = c;
|
|
return f;
|
|
}
|
|
if (spc_command[f].flag & 1) k = spc_command[f].b1;
|
|
if (spc_command[f].cmd < 0xca)
|
|
if (k)
|
|
m -= k;
|
|
else
|
|
o--;
|
|
}
|
|
printf("Misaligned music pointer");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
c = n;
|
|
i = h = m_free;
|
|
a -= addr;
|
|
m = 0;
|
|
k = 0;
|
|
o = 0;
|
|
|
|
for (g = addr; g < f;) {
|
|
spc_command2 = spc_command + i;
|
|
if (spc_command2->next == -1) {
|
|
l = m_size;
|
|
spc_command = current_spc_command_ = (SPCCommand *)realloc(
|
|
spc_command, sizeof(SPCCommand) * (m_size += 1024));
|
|
spc_command2 = spc_command + i;
|
|
n = l + 1023;
|
|
while (l < n) spc_command[l].next = l + 1, l++;
|
|
spc_command[l].next = -1;
|
|
n -= 1023;
|
|
while (l > n) spc_command[l].prev = l - 1, l--;
|
|
spc_command[l].prev = i;
|
|
spc_command2->next = l;
|
|
}
|
|
spc_command2->addr = g;
|
|
b = a[g];
|
|
if (!b) break;
|
|
if (m >= t && b != 0xf9) break;
|
|
g++;
|
|
j = 0;
|
|
if (b < 128) {
|
|
j = 1;
|
|
k = spc_command2->b1 = b;
|
|
b = a[g++];
|
|
if (b < 128) j = 3, spc_command2->b2 = b, b = a[g++];
|
|
}
|
|
if (b < 0xe0)
|
|
if (k)
|
|
m += k;
|
|
else
|
|
o++;
|
|
spc_command2->cmd = b;
|
|
spc_command2->flag = j;
|
|
if (b >= 0xe0) {
|
|
b -= 0xe0;
|
|
if (op_len[b]) spc_command2->p1 = a[g++];
|
|
if (op_len[b] > 1) spc_command2->p2 = a[g++];
|
|
if (op_len[b] > 2) spc_command2->p3 = a[g++];
|
|
if (b == 15) {
|
|
m_free = spc_command2->next;
|
|
spc_command[spc_command2->next].prev = -1;
|
|
l = LoadSPCCommand(rom, *(short *)(&(spc_command2->p1)), bank, t - m);
|
|
spc_command = current_spc_command_;
|
|
spc_command2 = spc_command + i;
|
|
*(short *)(&(spc_command2->p1)) = l;
|
|
spc_command2->next = m_free;
|
|
spc_command[spc_command2->next].prev = i;
|
|
GetBlockTime(rom, l, 0);
|
|
if (spc_command[l].flag & 4)
|
|
m +=
|
|
(spc_command[l].tim + spc_command[l].tim2 * k) * spc_command2->p3;
|
|
else {
|
|
i = spc_command2->next;
|
|
break;
|
|
}
|
|
if (song_range_[lastsr].endtime) k = song_range_[lastsr].endtime;
|
|
}
|
|
}
|
|
i = spc_command2->next;
|
|
}
|
|
|
|
spc_command[h].tim = m;
|
|
spc_command[h].tim2 = o;
|
|
spc_command[h].flag |= 4;
|
|
|
|
if (f == g && m < t) {
|
|
l = spc_command[i].prev;
|
|
lastsr = c;
|
|
spc_command[sr[lastsr].first].prev = l;
|
|
l = spc_command[l].next = sr[lastsr].first;
|
|
if (spc_command[l].flag & 4)
|
|
spc_command[h].tim = spc_command[l].tim + m + spc_command[l].tim2 * k,
|
|
spc_command[h].flag |= 4;
|
|
sr[lastsr].first = h;
|
|
sr[lastsr].start = addr;
|
|
sr[lastsr].inst++;
|
|
} else {
|
|
if (srsize == srnum)
|
|
song_range_ =
|
|
(SongRange *)realloc(song_range_, (srsize += 16) * sizeof(SongRange));
|
|
|
|
lastsr = srnum;
|
|
sr = song_range_ + (srnum++);
|
|
sr->start = addr;
|
|
sr->end = g;
|
|
sr->first = h;
|
|
sr->endtime = k;
|
|
sr->inst = 1;
|
|
sr->editor = 0;
|
|
sr->bank = d;
|
|
spc_command[spc_command[i].prev].next = -1;
|
|
}
|
|
|
|
spc_command[i].prev = -1;
|
|
m_free = i;
|
|
|
|
return h;
|
|
}
|
|
|
|
// =============================================================================
|
|
|
|
void Tracker::LoadSongs(ROM &rom) {
|
|
unsigned char *b;
|
|
unsigned char *c;
|
|
unsigned char *d;
|
|
short *e;
|
|
|
|
Song song;
|
|
Song song2;
|
|
SongPart *sp;
|
|
SPCCommand *spc_command;
|
|
ZeldaWave *zelda_wave;
|
|
|
|
int i;
|
|
int j;
|
|
int k;
|
|
int l = 0;
|
|
int m;
|
|
int n;
|
|
int o;
|
|
int p;
|
|
int q;
|
|
int r;
|
|
int t;
|
|
int u;
|
|
int range;
|
|
int filter;
|
|
|
|
spc_command = current_spc_command_ =
|
|
(SPCCommand *)malloc(1024 * sizeof(SPCCommand));
|
|
m_free = 0;
|
|
m_size = 1024;
|
|
srnum = 0;
|
|
srsize = 0;
|
|
song_range_ = 0;
|
|
sp_mark = 0;
|
|
b = rom.data();
|
|
|
|
sbank_ofs[1] = (b[0x91c] << 15) + ((b[0x918] & 127) << 8) + b[0x914];
|
|
sbank_ofs[3] = (b[0x93a] << 15) + ((b[0x936] & 127) << 8) + b[0x932];
|
|
|
|
for (i = 0; i < 1024; i++) {
|
|
spc_command[i].next = i + 1;
|
|
spc_command[i].prev = i - 1;
|
|
}
|
|
|
|
// Init blank songs.
|
|
for (i = 0; i < 128; i++) {
|
|
Song new_song;
|
|
songs.emplace_back(new_song);
|
|
}
|
|
|
|
spc_command[1023].next = -1;
|
|
for (i = 0; i < 3; i++) {
|
|
// Extract the song banks.
|
|
b = GetSPCAddr(rom, 0xd000, i);
|
|
for (j = 0;; j++) {
|
|
if ((r = ((unsigned short *)b)[j]) >= 0xd000) {
|
|
r = (r - 0xd000) >> 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
numsong[i] = r;
|
|
for (j = 0; j < r; j++) {
|
|
k = ((unsigned short *)b)[j];
|
|
if (!k)
|
|
songs[l].in_use = false;
|
|
else {
|
|
c = GetSPCAddr(rom, k, i);
|
|
|
|
// Init the bank index we are current loading.
|
|
if (!spcbank)
|
|
m = 0;
|
|
else
|
|
m = l - j;
|
|
|
|
for (; m < l; m++)
|
|
if (songs[m].in_use && songs[m].addr == k) {
|
|
(songs[l] = songs[m]).inst++;
|
|
|
|
break;
|
|
}
|
|
|
|
if (m == l) {
|
|
// create a new song (Song *)malloc(sizeof(Song));
|
|
songs[l] = song;
|
|
song.inst = 1;
|
|
song.addr = k;
|
|
song.flag = !spcbank;
|
|
|
|
for (m = 0;; m++)
|
|
if ((n = ((unsigned short *)c)[m]) < 256) break;
|
|
|
|
if (n > 0) {
|
|
song.flag |= 2;
|
|
song.lopst = (((unsigned short *)c)[m + 1] - k) >> 1;
|
|
}
|
|
|
|
song.numparts = m;
|
|
song.tbl = (SongPart **)malloc(4 * m);
|
|
|
|
for (m = 0; m < song.numparts; m++) {
|
|
k = ((unsigned short *)c)[m];
|
|
d = GetSPCAddr(rom, k, i);
|
|
if (!spcbank)
|
|
n = 0;
|
|
else
|
|
n = l - j;
|
|
|
|
for (; n < l; n++) {
|
|
song2 = songs[n];
|
|
if (song2.in_use)
|
|
for (o = 0; o < song2.numparts; o++)
|
|
if (song2.tbl[o]->addr == k) {
|
|
(song.tbl[m] = song2.tbl[o])->inst++;
|
|
goto foundpart;
|
|
}
|
|
}
|
|
|
|
for (o = 0; o < m; o++)
|
|
if (song.tbl[o]->addr == k) {
|
|
(song.tbl[m] = song.tbl[o])->inst++;
|
|
goto foundpart;
|
|
}
|
|
|
|
sp = song.tbl[m] = (SongPart *)malloc(sizeof(SongPart));
|
|
sp->flag = !spcbank;
|
|
sp->inst = 1;
|
|
sp->addr = k;
|
|
p = 50000;
|
|
for (o = 0; o < 8; o++) {
|
|
q = sp->tbl[o] =
|
|
LoadSPCCommand(rom, ((unsigned short *)d)[o], i, p);
|
|
spc_command = current_spc_command_ + q;
|
|
if ((spc_command->flag & 4) && spc_command->tim < p)
|
|
p = spc_command->tim;
|
|
}
|
|
foundpart:;
|
|
}
|
|
}
|
|
}
|
|
l++;
|
|
}
|
|
}
|
|
|
|
b = GetSPCAddr(rom, 0x800, 0);
|
|
snddat1 = (char *)malloc(spclen);
|
|
sndlen1 = spclen;
|
|
memcpy(snddat1, b, spclen);
|
|
|
|
b = GetSPCAddr(rom, 0x17c0, 0);
|
|
snddat2 = (char *)malloc(spclen);
|
|
sndlen2 = spclen;
|
|
memcpy(snddat2, b, spclen);
|
|
|
|
b = GetSPCAddr(rom, 0x3d00, 0);
|
|
insts = (ZeldaInstrument *)malloc(spclen);
|
|
memcpy(insts, b, spclen);
|
|
numinst = spclen / 6;
|
|
|
|
b = GetSPCAddr(rom, 0x3e00, 0);
|
|
m_ofs = b - rom.data() + spclen;
|
|
sndinsts = (ZeldaSfxInstrument *)malloc(spclen);
|
|
memcpy(sndinsts, b, spclen);
|
|
numsndinst = spclen / 9;
|
|
|
|
b = GetSPCAddr(rom, 0x3c00, 0);
|
|
zelda_wave = waves = (ZeldaWave *)malloc(sizeof(ZeldaWave) * (spclen >> 2));
|
|
p = spclen >> 1;
|
|
|
|
for (i = 0; i < p; i += 2) {
|
|
j = ((unsigned short *)b)[i];
|
|
|
|
if (j == 65535) break;
|
|
|
|
for (k = 0; k < i; k += 2) {
|
|
if (((unsigned short *)b)[k] == j) {
|
|
zelda_wave->copy = (short)(k >> 1);
|
|
goto foundwave;
|
|
}
|
|
}
|
|
|
|
zelda_wave->copy = -1;
|
|
|
|
foundwave:
|
|
|
|
d = GetSPCAddr(rom, j, 0);
|
|
e = (short *)malloc(2048);
|
|
|
|
k = 0;
|
|
l = 1024;
|
|
u = t = 0;
|
|
|
|
for (;;) {
|
|
m = *(d++);
|
|
|
|
range = (m >> 4) + 8;
|
|
filter = (m & 12) >> 2;
|
|
|
|
for (n = 0; n < 8; n++) {
|
|
o = (*d) >> 4;
|
|
|
|
if (o > 7) o -= 16;
|
|
|
|
o <<= range;
|
|
|
|
if (filter)
|
|
o += (t * fil1[filter] >> fil2[filter]) -
|
|
((u & -256) * fil3[filter] >> 4);
|
|
|
|
if (o > 0x7fffff) o = 0x7fffff;
|
|
|
|
if (o < -0x800000) o = -0x800000;
|
|
|
|
u = o;
|
|
|
|
// \code if(t>0x7fffff) t=0x7fffff;
|
|
// \code if(t < -0x800000) t=-0x800000;
|
|
|
|
e[k++] = o >> 8;
|
|
|
|
o = *(d++) & 15;
|
|
|
|
if (o > 7) o -= 16;
|
|
|
|
o <<= range;
|
|
|
|
if (filter)
|
|
o += (u * fil1[filter] >> fil2[filter]) -
|
|
((t & -256) * fil3[filter] >> 4);
|
|
|
|
if (o > 0x7fffff) o = 0x7fffff;
|
|
|
|
if (o < -0x800000) o = -0x800000;
|
|
|
|
t = o;
|
|
// \code if(u>0x7fffff) u=0x7fffff;
|
|
// \code if(u < -0x800000) u= -0x800000;
|
|
e[k++] = o >> 8;
|
|
}
|
|
|
|
if (m & 1) {
|
|
zelda_wave->lflag = (m & 2) >> 1;
|
|
break;
|
|
}
|
|
if (k == l) {
|
|
l += 1024;
|
|
e = (short *)realloc(e, l << 1);
|
|
}
|
|
}
|
|
|
|
e = zelda_wave->buf = (short *)realloc(e, (k + 1) << 1);
|
|
|
|
zelda_wave->lopst = (((unsigned short *)b)[i + 1] - j) * 16 / 9;
|
|
|
|
if (zelda_wave->lflag)
|
|
e[k] = e[zelda_wave->lopst];
|
|
else
|
|
e[k] = 0;
|
|
|
|
zelda_wave->end = k;
|
|
|
|
zelda_wave++;
|
|
}
|
|
|
|
numwave = i >> 1;
|
|
m_loaded = 1;
|
|
w_modf = 0;
|
|
}
|
|
|
|
short Tracker::SaveSPCCommand(ROM &rom, short num, short songtime,
|
|
short endtr) {
|
|
SPCCommand *spc_command = current_spc_command_;
|
|
SPCCommand *spc_command2;
|
|
SongRange *sr = song_range_;
|
|
SongSPCBlock *sbl;
|
|
|
|
text_buf_ty buf;
|
|
|
|
unsigned char *b;
|
|
int i = num;
|
|
int j = 0;
|
|
int k = 0;
|
|
int l = 0;
|
|
int m = 0;
|
|
int n = 0;
|
|
int o = 0;
|
|
int p = 0;
|
|
|
|
if (i == -1) return 0;
|
|
|
|
if (i >= m_size) {
|
|
printf("Error.\n");
|
|
m_modf = 1;
|
|
return 0;
|
|
}
|
|
|
|
if (spc_command[i].flag & 8) return spc_command[i].addr;
|
|
|
|
for (;;) {
|
|
j = spc_command[i].prev;
|
|
if (j == -1) break;
|
|
i = j;
|
|
}
|
|
|
|
for (j = 0; j < srnum; j++) {
|
|
if (sr[j].first == i) {
|
|
l = GetBlockTime(rom, i, 0);
|
|
m = i;
|
|
for (;;) {
|
|
if (m == -1) break;
|
|
k++;
|
|
spc_command2 = spc_command + m;
|
|
if (spc_command2->flag & 1) k++, n = spc_command2->b1;
|
|
if (spc_command2->flag & 2) k++;
|
|
if (spc_command2->cmd >= 0xe0) k += op_len[spc_command2->cmd - 0xe0];
|
|
m = spc_command2->next;
|
|
}
|
|
songtime -= l;
|
|
|
|
if (songtime > 0) {
|
|
l = (songtime + 126) / 127;
|
|
if (songtime % l) l += 2;
|
|
l++;
|
|
if (n && !songtime % n) {
|
|
p = songtime / n;
|
|
if (p < l) l = p;
|
|
} else
|
|
p = -1;
|
|
k += l;
|
|
}
|
|
k++;
|
|
sbl = AllocSPCBlock(k, sr[j].bank | ((!endtr) << 3) | 16);
|
|
b = sbl->buf;
|
|
|
|
for (;;) {
|
|
if (i == -1) break;
|
|
spc_command2 = spc_command + i;
|
|
spc_command2->addr = b - sbl->buf + sbl->start;
|
|
spc_command2->flag |= 8;
|
|
if (spc_command2->flag & 1) *(b++) = spc_command2->b1;
|
|
if (spc_command2->flag & 2) *(b++) = spc_command2->b2;
|
|
*(b++) = spc_command2->cmd;
|
|
if (spc_command2->cmd >= 0xe0) {
|
|
o = op_len[spc_command2->cmd - 0xe0];
|
|
if (spc_command2->cmd == 0xef) {
|
|
*(short *)b =
|
|
SaveSPCCommand(rom, *(short *)&(spc_command2->p1), 0, 1);
|
|
if (b) AddSPCReloc(sbl, b - sbl->buf);
|
|
b[2] = spc_command2->p3;
|
|
b += 3;
|
|
} else {
|
|
if (o) *(b++) = spc_command2->p1;
|
|
if (o > 1) *(b++) = spc_command2->p2;
|
|
if (o > 2) *(b++) = spc_command2->p3;
|
|
}
|
|
}
|
|
i = spc_command2->next;
|
|
}
|
|
|
|
if (songtime > 0) {
|
|
if (l != p) {
|
|
l = (songtime + 126) / 127;
|
|
if (songtime % l)
|
|
n = 127;
|
|
else
|
|
n = songtime / l;
|
|
*(b++) = n;
|
|
}
|
|
|
|
for (; songtime >= n; songtime -= n) *(b++) = 0xc9;
|
|
|
|
if (songtime) {
|
|
*(b++) = (uint8_t)songtime;
|
|
*(b++) = 0xc9;
|
|
}
|
|
}
|
|
*(b++) = 0;
|
|
return spc_command[num].addr;
|
|
}
|
|
}
|
|
|
|
printf("Address %04X not found", num);
|
|
|
|
printf("Error");
|
|
|
|
m_modf = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// =============================================================================
|
|
|
|
int Tracker::WriteSPCData(ROM &rom, void *buf, int len, int addr, int spc,
|
|
int limit) {
|
|
unsigned char *rom_data = rom.data();
|
|
|
|
if (!len) return addr;
|
|
|
|
if (((addr + len + 4) & 0x7fff) > 0x7ffb) {
|
|
if (addr + 5 > limit) goto error;
|
|
*(int *)(rom_data + addr) = 0x00010140;
|
|
rom_data[addr + 4] = 0xff;
|
|
addr += 5;
|
|
}
|
|
|
|
if (addr + len + 4 > limit) {
|
|
error:
|
|
printf("Not enough space for sound data");
|
|
m_modf = 1;
|
|
return 0xc8000;
|
|
}
|
|
|
|
*(short *)(rom_data + addr) = len;
|
|
*(short *)(rom_data + addr + 2) = spc;
|
|
|
|
memcpy(rom_data + addr + 4, buf, len);
|
|
|
|
return addr + len + 4;
|
|
}
|
|
|
|
// =============================================================================
|
|
|
|
void Tracker::SaveSongs(ROM &rom) {
|
|
int i;
|
|
int j;
|
|
int k;
|
|
int l = 0;
|
|
int m;
|
|
int n;
|
|
int o;
|
|
int p;
|
|
int q;
|
|
int r;
|
|
int t;
|
|
int u;
|
|
int v;
|
|
int w;
|
|
int a;
|
|
int e;
|
|
int f;
|
|
int g;
|
|
unsigned short bank_next[4];
|
|
unsigned short bank_lwr[4];
|
|
short *c;
|
|
short *d;
|
|
unsigned char *rom_data;
|
|
unsigned char *b;
|
|
|
|
Song song;
|
|
|
|
SPCCommand *spc_command;
|
|
|
|
SongPart *sp;
|
|
|
|
SongSPCBlock *stbl;
|
|
SongSPCBlock *sptbl;
|
|
SongSPCBlock *trtbl;
|
|
SongSPCBlock *pstbl;
|
|
|
|
ZeldaWave *zelda_wave;
|
|
ZeldaWave *zelda_wave2;
|
|
|
|
ZeldaInstrument *zi;
|
|
|
|
SampleEdit *sed;
|
|
|
|
short wtbl[128];
|
|
short x[16];
|
|
short y[18];
|
|
unsigned char z[64];
|
|
|
|
ss_num = 0;
|
|
ss_size = 512;
|
|
ss_next = 0;
|
|
|
|
// if the music has not been modified, return.
|
|
if (!(m_modf)) return;
|
|
|
|
ssblt = (SongSPCBlock **)malloc(512 * sizeof(SongSPCBlock));
|
|
|
|
// set it so the music has not been modified. (reset the status)
|
|
m_modf = 0;
|
|
rom_data = rom.data();
|
|
|
|
// SetCursor(wait_cursor);
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
k = numsong[i];
|
|
|
|
for (j = 0; j < k; j++) {
|
|
song = songs[l++];
|
|
|
|
if (!song.in_use) continue;
|
|
|
|
song.flag &= -5;
|
|
|
|
for (m = 0; m < song.numparts; m++) {
|
|
sp = song.tbl[m];
|
|
sp->flag &= -3;
|
|
}
|
|
}
|
|
}
|
|
|
|
j = m_size;
|
|
spc_command = current_spc_command_;
|
|
|
|
for (i = 0; i < j; i++) {
|
|
spc_command->flag &= -13;
|
|
spc_command++;
|
|
}
|
|
|
|
l = 0;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
k = numsong[i];
|
|
stbl = AllocSPCBlock(k << 1, i + 1);
|
|
|
|
for (j = 0; j < k; j++) {
|
|
song = songs[l++];
|
|
|
|
if (!song.in_use) {
|
|
((short *)(stbl->buf))[j] = 0;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (song.flag & 4) goto alreadysaved;
|
|
|
|
sptbl = AllocSPCBlock(((song.numparts + 1) << 1) + (song.flag & 2),
|
|
(song.flag & 1) ? 0 : (i + 1));
|
|
|
|
for (m = 0; m < song.numparts; m++) {
|
|
sp = song.tbl[m];
|
|
|
|
if (sp->flag & 2) goto spsaved;
|
|
|
|
trtbl = AllocSPCBlock(16, (sp->flag & 1) ? 0 : (i + 1));
|
|
|
|
p = 0;
|
|
|
|
for (n = 0; n < 8; n++) {
|
|
o = GetBlockTime(rom, sp->tbl[n], 0);
|
|
|
|
if (o > p) p = o;
|
|
}
|
|
|
|
q = 1;
|
|
|
|
for (n = 0; n < 8; n++) {
|
|
core::stle16b_i(trtbl->buf, n, SaveSPCCommand(rom, sp->tbl[n], p, q));
|
|
|
|
if (core::ldle16b_i(trtbl->buf, n)) AddSPCReloc(trtbl, n << 1), q = 0;
|
|
}
|
|
|
|
sp->addr = trtbl->start;
|
|
sp->flag |= 2;
|
|
spsaved:
|
|
((short *)(sptbl->buf))[m] = sp->addr;
|
|
|
|
AddSPCReloc(sptbl, m << 1);
|
|
}
|
|
|
|
if (song.flag & 2) {
|
|
((short *)(sptbl->buf))[m++] = 255;
|
|
((short *)(sptbl->buf))[m] = sptbl->start + (song.lopst << 1);
|
|
|
|
AddSPCReloc(sptbl, m << 1);
|
|
} else
|
|
((short *)(sptbl->buf))[m++] = 0;
|
|
|
|
song.addr = sptbl->start;
|
|
song.flag |= 4;
|
|
alreadysaved:
|
|
((short *)(stbl->buf))[j] = song.addr;
|
|
|
|
AddSPCReloc(stbl, j << 1);
|
|
}
|
|
}
|
|
|
|
if (w_modf) {
|
|
b = (uint8_t *)malloc(0xc000);
|
|
j = 0;
|
|
|
|
zelda_wave = waves;
|
|
|
|
// if (mbanks[3])
|
|
// sed = (SampleEdit *)GetWindowLongPtr(mbanks[3], GWLP_USERDATA);
|
|
// else
|
|
// sed = 0;
|
|
|
|
for (i = 0; i < numwave; i++, zelda_wave++) {
|
|
if (zelda_wave->copy != -1) continue;
|
|
|
|
wtbl[i << 1] = j + 0x4000;
|
|
|
|
if (zelda_wave->lflag) {
|
|
l = zelda_wave->end - zelda_wave->lopst;
|
|
|
|
if (l & 15) {
|
|
k = (l << 15) / ((l + 15) & -16);
|
|
p = (zelda_wave->end << 15) / k;
|
|
c = (short *)malloc(p << 1);
|
|
n = 0;
|
|
d = zelda_wave->buf;
|
|
|
|
for (m = 0;;) {
|
|
c[n++] = (d[m >> 15] * ((m & 32767) ^ 32767) +
|
|
d[(m >> 15) + 1] * (m & 32767)) /
|
|
32767;
|
|
|
|
m += k;
|
|
|
|
if (n >= p) break;
|
|
}
|
|
|
|
zelda_wave->lopst = (zelda_wave->lopst << 15) / k;
|
|
zelda_wave->end = p;
|
|
zelda_wave->buf =
|
|
(short *)realloc(zelda_wave->buf, (zelda_wave->end + 1) << 1);
|
|
memcpy(zelda_wave->buf, c, zelda_wave->end << 1);
|
|
free(c);
|
|
zelda_wave->buf[zelda_wave->end] = zelda_wave->buf[zelda_wave->lopst];
|
|
zelda_wave2 = waves;
|
|
|
|
for (m = 0; m < numwave; m++, zelda_wave2++)
|
|
if (zelda_wave2->copy == i)
|
|
zelda_wave2->lopst = zelda_wave2->lopst << 15 / k;
|
|
|
|
zi = insts;
|
|
|
|
for (m = 0; m < numinst; m++) {
|
|
n = zi->samp;
|
|
|
|
if (n >= numwave) continue;
|
|
|
|
if (n == i || waves[n].copy == i) {
|
|
o = (zi->multhi << 8) + zi->multlo;
|
|
o = (o << 15) / k;
|
|
zi->multlo = o;
|
|
zi->multhi = o >> 8;
|
|
|
|
if (sed && sed->editinst == m) {
|
|
sed->init = 1;
|
|
// SetDlgItemInt(sed->dlg, 3014, o, 0);
|
|
sed->init = 0;
|
|
}
|
|
}
|
|
|
|
zi++;
|
|
}
|
|
|
|
// Modifywaves(rom, i);
|
|
}
|
|
}
|
|
|
|
k = (-zelda_wave->end) & 15;
|
|
d = zelda_wave->buf;
|
|
n = 0;
|
|
wtbl[(i << 1) + 1] = ((zelda_wave->lopst + k) >> 4) * 9 + wtbl[i << 1];
|
|
y[0] = y[1] = 0;
|
|
u = 4;
|
|
|
|
for (;;) {
|
|
for (o = 0; o < 16; o++) {
|
|
if (k)
|
|
k--, x[o] = 0;
|
|
else
|
|
x[o] = d[n++];
|
|
}
|
|
p = 0x7fffffff;
|
|
a = 0;
|
|
|
|
for (t = 0; t < 4; t++) {
|
|
r = 0;
|
|
for (o = 0; o < 16; o++) {
|
|
l = x[o];
|
|
y[o + 2] = l;
|
|
l += (y[o] * fil3[t] >> 4) - (y[o + 1] * fil1[t] >> fil2[t]);
|
|
if (l > r)
|
|
r = l;
|
|
else if (-l > r)
|
|
r = -l;
|
|
}
|
|
r <<= 1;
|
|
if (t)
|
|
m = 14;
|
|
else
|
|
m = 15;
|
|
for (q = 0; q < 12; q++, m += m)
|
|
if (m >= r) break;
|
|
tryagain:
|
|
if (q && (q < 12 || m == r))
|
|
v = (1 << (q - 1)) - 1;
|
|
else
|
|
v = 0;
|
|
m = 0;
|
|
for (o = 0; o < 16; o++) {
|
|
l = (y[o + 1] * fil1[t] >> fil2[t]) - (y[o] * fil3[t] >> 4);
|
|
w = x[o];
|
|
r = (w - l + v) >> q;
|
|
if ((r + 8) & 0xfff0) {
|
|
q++;
|
|
a -= o;
|
|
goto tryagain;
|
|
}
|
|
z[a++] = r;
|
|
l = (r << q) + l;
|
|
y[o + 2] = l;
|
|
l -= w;
|
|
m += l * l;
|
|
}
|
|
if (u == 4) {
|
|
u = 0, e = q, f = y[16], g = y[17];
|
|
break;
|
|
}
|
|
if (m < p) p = m, u = t, e = q, f = y[16], g = y[17];
|
|
}
|
|
m = (e << 4) | (u << 2);
|
|
if (n == zelda_wave->end) m |= 1;
|
|
if (zelda_wave->lflag) m |= 2;
|
|
b[j++] = m;
|
|
m = 0;
|
|
a = u << 4;
|
|
for (o = 0; o < 16; o++) {
|
|
m |= z[a++] & 15;
|
|
if (o & 1)
|
|
b[j++] = m, m = 0;
|
|
else
|
|
m <<= 4;
|
|
}
|
|
if (n == zelda_wave->end) break;
|
|
y[0] = f;
|
|
y[1] = g;
|
|
}
|
|
}
|
|
|
|
// if (sed) {
|
|
// SetDlgItemInt(sed->dlg, ID_Samp_SampleLengthEdit, sed->zelda_wave->end,
|
|
// 0); SetDlgItemInt(sed->dlg, ID_Samp_LoopPointEdit,
|
|
// sed->zelda_wave->lopst, 0);
|
|
|
|
// InvalidateRect(GetDlgItem(sed->dlg, ID_Samp_Display), 0, 1);
|
|
// }
|
|
|
|
zelda_wave = waves;
|
|
|
|
for (i = 0; i < numwave; i++, zelda_wave++) {
|
|
if (zelda_wave->copy != -1) {
|
|
wtbl[i << 1] = wtbl[zelda_wave->copy << 1];
|
|
wtbl[(i << 1) + 1] = (zelda_wave->lopst >> 4) * 9 + wtbl[i << 1];
|
|
}
|
|
}
|
|
|
|
m = WriteSPCData(rom, wtbl, numwave << 2, 0xc8000, 0x3c00, 0xd74fc);
|
|
m = WriteSPCData(rom, b, j, m, 0x4000, 0xd74fc);
|
|
|
|
free(b);
|
|
m = WriteSPCData(rom, insts, numinst * 6, m, 0x3d00, 0xd74fc);
|
|
m = WriteSPCData(rom, snddat1, sndlen1, m, 0x800, 0xd74fc);
|
|
m = WriteSPCData(rom, snddat2, sndlen2, m, 0x17c0, 0xd74fc);
|
|
m = WriteSPCData(rom, sndinsts, numsndinst * 9, m, 0x3e00, 0xd74fc);
|
|
m_ofs = m;
|
|
} else {
|
|
m = m_ofs;
|
|
}
|
|
|
|
bank_next[0] = 0x2880;
|
|
bank_next[1] = 0xd000;
|
|
bank_next[2] = 0xd000;
|
|
bank_next[3] = 0xd000;
|
|
bank_lwr[0] = 0x2880;
|
|
|
|
for (k = 0; k < 4; k++) {
|
|
pstbl = 0;
|
|
for (i = 0; i < ss_num; i++) {
|
|
stbl = ssblt[i];
|
|
if (stbl->bank != k) continue;
|
|
j = bank_next[k];
|
|
if (j + stbl->len > 0xffc0) {
|
|
if (k == 3)
|
|
j = 0x2880;
|
|
else
|
|
j = bank_next[0];
|
|
bank_lwr[k] = j;
|
|
pstbl = 0;
|
|
}
|
|
if (j + stbl->len > 0x3c00 && j < 0xd000) {
|
|
printf("Not enough space for music bank %d", k);
|
|
m_modf = 1;
|
|
return;
|
|
}
|
|
if (pstbl && (pstbl->flag & 1) && (stbl->flag & 2)) j--, pstbl->len--;
|
|
stbl->addr = j;
|
|
pstbl = stbl;
|
|
bank_next[k] = j + stbl->len;
|
|
}
|
|
}
|
|
for (i = 0; i < ss_num; i++) {
|
|
stbl = ssblt[i];
|
|
for (j = stbl->relnum - 1; j >= 0; j--) {
|
|
k = *(unsigned short *)(stbl->buf + stbl->relocs[j]);
|
|
for (l = 0; l < ss_num; l++) {
|
|
sptbl = ssblt[l];
|
|
if (sptbl->start <= k && sptbl->len > k - sptbl->start) goto noerror;
|
|
}
|
|
printf("Internal error");
|
|
m_modf = 1;
|
|
return;
|
|
noerror:
|
|
|
|
if (((!sptbl->bank) && stbl->bank < 3) || (sptbl->bank == stbl->bank)) {
|
|
*(unsigned short *)(stbl->buf + stbl->relocs[j]) =
|
|
sptbl->addr + k - sptbl->start;
|
|
} else {
|
|
printf("An address outside the bank was referenced.\n");
|
|
m_modf = 1;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
l = m;
|
|
for (k = 0; k < 4; k++) {
|
|
switch (k) {
|
|
case 1:
|
|
rom[0x914] = l;
|
|
rom[0x918] = (l >> 8) | 128;
|
|
rom[0x91c] = l >> 15;
|
|
break;
|
|
case 2:
|
|
l = 0xd8000;
|
|
break;
|
|
case 3:
|
|
l = m;
|
|
rom[0x932] = l;
|
|
rom[0x936] = (l >> 8) | 128;
|
|
rom[0x93a] = l >> 15;
|
|
break;
|
|
}
|
|
for (o = 0; o < 2; o++) {
|
|
n = l + 4;
|
|
for (i = 0; i < ss_num; i++) {
|
|
stbl = ssblt[i];
|
|
if (!stbl) continue;
|
|
if ((stbl->addr < 0xd000) ^ o) continue;
|
|
if (stbl->bank != k) continue;
|
|
if (n + stbl->len > ((k == 2) ? 0xdb7fc : 0xd74fc)) {
|
|
printf("Not enough space for music");
|
|
m_modf = 1;
|
|
return;
|
|
}
|
|
memcpy(rom.data() + n, stbl->buf, stbl->len);
|
|
n += stbl->len;
|
|
free(stbl->relocs);
|
|
free(stbl->buf);
|
|
free(stbl);
|
|
ssblt[i] = 0;
|
|
}
|
|
if (n > l + 4) {
|
|
*(short *)(rom + l) = n - l - 4;
|
|
*(short *)(rom + l + 2) = o ? bank_lwr[k] : 0xd000;
|
|
l = n;
|
|
}
|
|
}
|
|
*(short *)(rom + l) = 0;
|
|
*(short *)(rom + l + 2) = 0x800;
|
|
if (k == 1) m = l + 4;
|
|
}
|
|
free(ssblt);
|
|
}
|
|
|
|
// =============================================================================
|
|
|
|
void Tracker::EditTrack(ROM &rom, short i) {
|
|
int j, k, l;
|
|
SongRange *sr = song_range_;
|
|
SPCCommand *spc_command;
|
|
|
|
text_buf_ty buf;
|
|
|
|
// -----------------------------
|
|
|
|
k = srnum;
|
|
|
|
spc_command = current_spc_command_;
|
|
|
|
if (i == -1) return;
|
|
|
|
if (i >= m_size) {
|
|
printf("Invalid address: %04X", i);
|
|
goto error;
|
|
}
|
|
|
|
for (;;) {
|
|
if ((j = spc_command[i].prev) != -1)
|
|
i = j;
|
|
else
|
|
break;
|
|
}
|
|
|
|
for (l = 0; l < k; l++)
|
|
if (sr->first == i)
|
|
break;
|
|
else
|
|
sr++;
|
|
|
|
if (l == k) {
|
|
printf("Not found: %04X", i);
|
|
error:
|
|
printf("Error");
|
|
return;
|
|
}
|
|
|
|
// if (sr->editor)
|
|
// HM_MDI_ActivateChild(clientwnd, sr->editor);
|
|
// else
|
|
// Editwin(rom, "TRACKEDIT", "Song editor", l + (i << 16),
|
|
// sizeof(TRACKEDIT));
|
|
}
|
|
|
|
// CRITICAL_SECTION cs_song;
|
|
// =============================================================================
|
|
|
|
void Tracker::NewSR(ROM &rom, int bank) {
|
|
SPCCommand *spc_command;
|
|
SongRange *sr;
|
|
|
|
if (srnum == srsize) {
|
|
srsize += 16;
|
|
song_range_ = (SongRange *)realloc(song_range_, srsize * sizeof(SongRange));
|
|
}
|
|
|
|
sr = song_range_ + srnum;
|
|
srnum++;
|
|
sr->first = AllocSPCCommand();
|
|
sr->bank = bank;
|
|
sr->editor = 0;
|
|
spc_command = current_spc_command_ + sr->first;
|
|
spc_command->prev = -1;
|
|
spc_command->next = -1;
|
|
spc_command->cmd = 128;
|
|
spc_command->flag = 0;
|
|
EditTrack(rom, sr->first);
|
|
}
|
|
|
|
// =============================================================================
|
|
|
|
} // namespace zelda3
|
|
} // namespace app
|
|
} // namespace yaze
|