Merge branch 'master' into ay_divider
This commit is contained in:
commit
76997fd5ba
16 changed files with 577 additions and 27 deletions
|
|
@ -45,8 +45,8 @@
|
|||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev92" // it breaks compatiblity
|
||||
#define DIV_ENGINE_VERSION 92
|
||||
#define DIV_VERSION "dev93"
|
||||
#define DIV_ENGINE_VERSION 93
|
||||
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
|
|
@ -548,7 +548,7 @@ class DivEngine {
|
|||
DivInstrumentType getPreferInsSecondType(int ch);
|
||||
|
||||
// get song system name
|
||||
const char* getSongSystemName();
|
||||
String getSongSystemName(bool isMultiSystemAcceptable=true);
|
||||
|
||||
// get sys name
|
||||
const char* getSystemName(DivSystem sys);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "engine.h"
|
||||
#include "../ta-log.h"
|
||||
#include "instrument.h"
|
||||
#include "song.h"
|
||||
#include <zlib.h>
|
||||
#include <fmt/printf.h>
|
||||
|
|
@ -2034,11 +2035,28 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
return success;
|
||||
}
|
||||
|
||||
#define CHECK_BLOCK_VERSION(x) \
|
||||
if (blockVersion>x) { \
|
||||
logE("incompatible block version %d for %s!",blockVersion,blockName); \
|
||||
lastError="incompatible block version"; \
|
||||
delete[] file; \
|
||||
return false; \
|
||||
}
|
||||
|
||||
bool DivEngine::loadFTM(unsigned char* file, size_t len) {
|
||||
SafeReader reader=SafeReader(file,len);
|
||||
warnings="";
|
||||
try {
|
||||
DivSong ds;
|
||||
String blockName;
|
||||
unsigned char expansions=0;
|
||||
unsigned int tchans=0;
|
||||
unsigned int n163Chans=0;
|
||||
bool hasSequence[256][8];
|
||||
unsigned char sequenceIndex[256][8];
|
||||
|
||||
memset(hasSequence,0,256*8*sizeof(bool));
|
||||
memset(sequenceIndex,0,256*8);
|
||||
|
||||
if (!reader.seek(18,SEEK_SET)) {
|
||||
logE("premature end of file!");
|
||||
|
|
@ -2046,17 +2064,327 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) {
|
|||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
ds.version=(unsigned short)reader.readS();
|
||||
ds.version=(unsigned short)reader.readI();
|
||||
logI("module version %d (0x%.4x)",ds.version,ds.version);
|
||||
|
||||
if (ds.version>0x0440) {
|
||||
if (ds.version>0x0450) {
|
||||
logE("incompatible version %x!",ds.version);
|
||||
lastError="incompatible version";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
while (true) {
|
||||
blockName=reader.readString(3);
|
||||
if (blockName=="END") {
|
||||
// end of module
|
||||
logD("end of data");
|
||||
break;
|
||||
}
|
||||
|
||||
// not the end
|
||||
reader.seek(-3,SEEK_CUR);
|
||||
blockName=reader.readString(16);
|
||||
unsigned int blockVersion=(unsigned int)reader.readI();
|
||||
unsigned int blockSize=(unsigned int)reader.readI();
|
||||
size_t blockStart=reader.tell();
|
||||
|
||||
logD("reading block %s (version %d, %d bytes)",blockName,blockVersion,blockSize);
|
||||
if (blockName=="PARAMS") {
|
||||
CHECK_BLOCK_VERSION(6);
|
||||
unsigned int oldSpeedTempo=0;
|
||||
if (blockVersion<=1) {
|
||||
oldSpeedTempo=reader.readI();
|
||||
}
|
||||
if (blockVersion>=2) {
|
||||
expansions=reader.readC();
|
||||
}
|
||||
tchans=reader.readI();
|
||||
unsigned int pal=reader.readI();
|
||||
unsigned int customHz=reader.readI();
|
||||
unsigned int newVibrato=0;
|
||||
unsigned int speedSplitPoint=0;
|
||||
if (blockVersion>=3) {
|
||||
newVibrato=reader.readI();
|
||||
}
|
||||
if (blockVersion>=4) {
|
||||
ds.hilightA=reader.readI();
|
||||
ds.hilightB=reader.readI();
|
||||
}
|
||||
if (expansions&8) if (blockVersion>=5) { // N163 channels
|
||||
n163Chans=reader.readI();
|
||||
}
|
||||
if (blockVersion>=6) {
|
||||
speedSplitPoint=reader.readI();
|
||||
}
|
||||
|
||||
logV("old speed/tempo: %d",oldSpeedTempo);
|
||||
logV("expansions: %x",expansions);
|
||||
logV("channels: %d",tchans);
|
||||
logV("PAL: %d",pal);
|
||||
logV("custom Hz: %d",customHz);
|
||||
logV("new vibrato: %d",newVibrato);
|
||||
logV("N163 channels: %d",n163Chans);
|
||||
logV("highlight 1: %d",ds.hilightA);
|
||||
logV("highlight 2: %d",ds.hilightB);
|
||||
logV("split point: %d",speedSplitPoint);
|
||||
|
||||
if (customHz!=0) {
|
||||
ds.hz=customHz;
|
||||
}
|
||||
|
||||
// initialize channels
|
||||
int systemID=0;
|
||||
ds.system[systemID++]=DIV_SYSTEM_NES;
|
||||
if (expansions&1) {
|
||||
ds.system[systemID++]=DIV_SYSTEM_VRC6;
|
||||
}
|
||||
if (expansions&2) {
|
||||
ds.system[systemID++]=DIV_SYSTEM_VRC7;
|
||||
}
|
||||
if (expansions&4) {
|
||||
ds.system[systemID++]=DIV_SYSTEM_FDS;
|
||||
}
|
||||
if (expansions&8) {
|
||||
ds.system[systemID++]=DIV_SYSTEM_MMC5;
|
||||
}
|
||||
if (expansions&16) {
|
||||
ds.system[systemID]=DIV_SYSTEM_N163;
|
||||
ds.systemFlags[systemID++]=n163Chans;
|
||||
}
|
||||
if (expansions&32) {
|
||||
ds.system[systemID]=DIV_SYSTEM_AY8910;
|
||||
ds.systemFlags[systemID++]=38; // Sunsoft 5B
|
||||
}
|
||||
ds.systemLen=systemID;
|
||||
|
||||
unsigned int calcChans=0;
|
||||
for (int i=0; i<ds.systemLen; i++) {
|
||||
calcChans+=getChannelCount(ds.system[i]);
|
||||
}
|
||||
if (calcChans!=tchans) {
|
||||
logE("channel counts do not match! %d != %d",tchans,calcChans);
|
||||
lastError="channel counts do not match";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (tchans>DIV_MAX_CHANS) {
|
||||
tchans=DIV_MAX_CHANS;
|
||||
logW("too many channels!");
|
||||
}
|
||||
} else if (blockName=="INFO") {
|
||||
CHECK_BLOCK_VERSION(1);
|
||||
ds.name=reader.readString(32);
|
||||
ds.author=reader.readString(32);
|
||||
ds.copyright=reader.readString(32);
|
||||
} else if (blockName=="HEADER") {
|
||||
CHECK_BLOCK_VERSION(3);
|
||||
unsigned char totalSongs=reader.readC();
|
||||
logV("%d songs:",totalSongs+1);
|
||||
for (int i=0; i<=totalSongs; i++) {
|
||||
String subSongName=reader.readString();
|
||||
logV("- %s",subSongName);
|
||||
}
|
||||
for (unsigned int i=0; i<tchans; i++) {
|
||||
unsigned char chID=reader.readC();
|
||||
logV("for channel ID %d",chID);
|
||||
for (int j=0; j<=totalSongs; j++) {
|
||||
unsigned char effectCols=reader.readC();
|
||||
if (j==0) {
|
||||
ds.pat[i].effectCols=effectCols+1;
|
||||
}
|
||||
logV("- song %d has %d effect columns",j,effectCols);
|
||||
}
|
||||
}
|
||||
} else if (blockName=="INSTRUMENTS") {
|
||||
CHECK_BLOCK_VERSION(6);
|
||||
ds.insLen=reader.readI();
|
||||
if (ds.insLen<0 || ds.insLen>256) {
|
||||
logE("too many instruments/out of range!");
|
||||
lastError="too many instruments/out of range";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i=0; i<ds.insLen; i++) {
|
||||
DivInstrument* ins=new DivInstrument;
|
||||
ds.ins.push_back(ins);
|
||||
}
|
||||
|
||||
logV("instruments:");
|
||||
for (int i=0; i<ds.insLen; i++) {
|
||||
unsigned int insIndex=reader.readI();
|
||||
if (insIndex>=ds.ins.size()) {
|
||||
logE("instrument index %d is out of range!",insIndex);
|
||||
lastError="instrument index out of range";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
||||
DivInstrument* ins=ds.ins[insIndex];
|
||||
unsigned char insType=reader.readC();
|
||||
switch (insType) {
|
||||
case 1:
|
||||
ins->type=DIV_INS_STD;
|
||||
break;
|
||||
case 2: // TODO: tell VRC6 and VRC6 saw instruments apart
|
||||
ins->type=DIV_INS_VRC6;
|
||||
break;
|
||||
case 3:
|
||||
ins->type=DIV_INS_OPLL;
|
||||
break;
|
||||
case 4:
|
||||
ins->type=DIV_INS_FDS;
|
||||
break;
|
||||
case 5:
|
||||
ins->type=DIV_INS_N163;
|
||||
break;
|
||||
case 6: // 5B?
|
||||
ins->type=DIV_INS_AY;
|
||||
break;
|
||||
default: {
|
||||
logE("%d: invalid instrument type %d",insIndex,insType);
|
||||
lastError="invalid instrument type";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// instrument data
|
||||
switch (ins->type) {
|
||||
case DIV_INS_STD: {
|
||||
unsigned int totalSeqs=reader.readI();
|
||||
if (totalSeqs>5) {
|
||||
logE("%d: too many sequences!",insIndex);
|
||||
lastError="too many sequences";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int j=0; j<totalSeqs; j++) {
|
||||
hasSequence[insIndex][j]=reader.readC();
|
||||
sequenceIndex[insIndex][j]=reader.readC();
|
||||
}
|
||||
|
||||
const int dpcmNotes=(blockVersion>=2)?96:72;
|
||||
for (int j=0; j<dpcmNotes; j++) {
|
||||
ins->amiga.noteMap[j]=(short)((unsigned char)reader.readC())-1;
|
||||
ins->amiga.noteFreq[j]=(unsigned char)reader.readC();
|
||||
if (blockVersion>=6) {
|
||||
reader.readC(); // DMC value
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_INS_VRC6: {
|
||||
unsigned int totalSeqs=reader.readI();
|
||||
if (totalSeqs>4) {
|
||||
logE("%d: too many sequences!",insIndex);
|
||||
lastError="too many sequences";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int j=0; j<totalSeqs; j++) {
|
||||
hasSequence[insIndex][j]=reader.readC();
|
||||
sequenceIndex[insIndex][j]=reader.readC();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_INS_OPLL: {
|
||||
ins->fm.opllPreset=(unsigned int)reader.readI();
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
case DIV_INS_FDS: {
|
||||
DivWavetable* wave=new DivWavetable;
|
||||
wave->len=64;
|
||||
wave->max=64;
|
||||
for (int j=0; j<64; j++) {
|
||||
wave->data[j]=reader.readC();
|
||||
}
|
||||
ins->std.waveMacro.len=1;
|
||||
ins->std.waveMacro.val[0]=ds.wave.size();
|
||||
for (int j=0; j<32; j++) {
|
||||
ins->fds.modTable[j]=reader.readC()-3;
|
||||
}
|
||||
ins->fds.modSpeed=reader.readI();
|
||||
ins->fds.modDepth=reader.readI();
|
||||
reader.readI(); // this is delay. currently ignored. TODO.
|
||||
ds.wave.push_back(wave);
|
||||
|
||||
ins->std.volMacro.len=reader.readC();
|
||||
ins->std.volMacro.loop=reader.readI();
|
||||
ins->std.volMacro.rel=reader.readI();
|
||||
reader.readI(); // arp mode does not apply here
|
||||
for (int j=0; j<ins->std.volMacro.len; j++) {
|
||||
ins->std.volMacro.val[j]=reader.readC();
|
||||
}
|
||||
|
||||
ins->std.arpMacro.len=reader.readC();
|
||||
ins->std.arpMacro.loop=reader.readI();
|
||||
ins->std.arpMacro.rel=reader.readI();
|
||||
ins->std.arpMacro.mode=reader.readI();
|
||||
for (int j=0; j<ins->std.arpMacro.len; j++) {
|
||||
ins->std.arpMacro.val[j]=reader.readC();
|
||||
}
|
||||
|
||||
ins->std.pitchMacro.len=reader.readC();
|
||||
ins->std.pitchMacro.loop=reader.readI();
|
||||
ins->std.pitchMacro.rel=reader.readI();
|
||||
reader.readI(); // arp mode does not apply here
|
||||
for (int j=0; j<ins->std.pitchMacro.len; j++) {
|
||||
ins->std.pitchMacro.val[j]=reader.readC();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case DIV_INS_N163: {
|
||||
// TODO!
|
||||
break;
|
||||
}
|
||||
// TODO: 5B!
|
||||
default: {
|
||||
logE("%d: what's going on here?",insIndex);
|
||||
lastError="invalid instrument type";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// name
|
||||
ins->name=reader.readString((unsigned int)reader.readI());
|
||||
logV("- %d: %s",insIndex,ins->name);
|
||||
}
|
||||
} else if (blockName=="SEQUENCES") {
|
||||
CHECK_BLOCK_VERSION(6);
|
||||
} else if (blockName=="FRAMES") {
|
||||
CHECK_BLOCK_VERSION(3);
|
||||
} else if (blockName=="PATTERNS") {
|
||||
CHECK_BLOCK_VERSION(5);
|
||||
} else if (blockName=="DPCM SAMPLES") {
|
||||
CHECK_BLOCK_VERSION(1);
|
||||
} else if (blockName=="SEQUENCES_VRC6") {
|
||||
// where are the 5B and FDS sequences?
|
||||
CHECK_BLOCK_VERSION(6);
|
||||
} else if (blockName=="SEQUENCES_N163") {
|
||||
CHECK_BLOCK_VERSION(1);
|
||||
} else if (blockName=="COMMENTS") {
|
||||
CHECK_BLOCK_VERSION(1);
|
||||
} else {
|
||||
logE("block %s is unknown!",blockName);
|
||||
lastError="unknown block "+blockName;
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((reader.tell()-blockStart)!=blockSize) {
|
||||
logE("block %s is incomplete!",blockName);
|
||||
lastError="incomplete block "+blockName;
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (EndOfFileException& e) {
|
||||
logE("premature end of file!");
|
||||
lastError="incomplete file";
|
||||
|
|
|
|||
|
|
@ -506,6 +506,20 @@ void DivInstrument::putInsData(SafeWriter* w) {
|
|||
|
||||
// C64 no test
|
||||
w->writeC(c64.noTest);
|
||||
|
||||
// MultiPCM
|
||||
w->writeC(multipcm.ar);
|
||||
w->writeC(multipcm.d1r);
|
||||
w->writeC(multipcm.dl);
|
||||
w->writeC(multipcm.d2r);
|
||||
w->writeC(multipcm.rr);
|
||||
w->writeC(multipcm.rc);
|
||||
w->writeC(multipcm.lfo);
|
||||
w->writeC(multipcm.vib);
|
||||
w->writeC(multipcm.am);
|
||||
for (int j=0; j<23; j++) { // reserved
|
||||
w->writeC(0);
|
||||
}
|
||||
}
|
||||
|
||||
DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
|
||||
|
|
@ -1014,6 +1028,21 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
|
|||
c64.noTest=reader.readC();
|
||||
}
|
||||
|
||||
// MultiPCM
|
||||
if (version>=93) {
|
||||
multipcm.ar=reader.readC();
|
||||
multipcm.d1r=reader.readC();
|
||||
multipcm.dl=reader.readC();
|
||||
multipcm.d2r=reader.readC();
|
||||
multipcm.rr=reader.readC();
|
||||
multipcm.rc=reader.readC();
|
||||
multipcm.lfo=reader.readC();
|
||||
multipcm.vib=reader.readC();
|
||||
multipcm.am=reader.readC();
|
||||
// reserved
|
||||
for (int k=0; k<23; k++) reader.readC();
|
||||
}
|
||||
|
||||
return DIV_DATA_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -338,6 +338,16 @@ struct DivInstrumentFDS {
|
|||
}
|
||||
};
|
||||
|
||||
struct DivInstrumentMultiPCM {
|
||||
unsigned char ar, d1r, dl, d2r, rr, rc;
|
||||
unsigned char lfo, vib, am;
|
||||
|
||||
DivInstrumentMultiPCM():
|
||||
ar(15), d1r(15), dl(0), d2r(0), rr(15), rc(15),
|
||||
lfo(0), vib(0), am(0) {
|
||||
}
|
||||
};
|
||||
|
||||
enum DivWaveSynthEffects {
|
||||
DIV_WS_NONE=0,
|
||||
// one waveform effects
|
||||
|
|
@ -393,6 +403,7 @@ struct DivInstrument {
|
|||
DivInstrumentAmiga amiga;
|
||||
DivInstrumentN163 n163;
|
||||
DivInstrumentFDS fds;
|
||||
DivInstrumentMultiPCM multipcm;
|
||||
DivInstrumentWaveSynth ws;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -480,7 +480,7 @@ if (m_choffs == 0)
|
|||
#endif
|
||||
|
||||
// early out if the envelope is effectively off
|
||||
if (m_env_attenuation > EG_QUIET)
|
||||
if (m_env_attenuation > EG_QUIET && m_cache.eg_shift == 0)
|
||||
return 0;
|
||||
|
||||
// get the absolute value of the sin, as attenuation, as a 4.8 fixed point value
|
||||
|
|
|
|||
|
|
@ -70,16 +70,24 @@
|
|||
// OPZ supports a "fixed frequency" mode for each operator, with a 3-bit
|
||||
// range and 4-bit frequency value, plus a 1-bit enable. Not sure how that
|
||||
// works at all, so it's not implemented.
|
||||
// note by tildearrow:
|
||||
// - I have verified behavior of this mode against real hardware.
|
||||
// after applying a small fix on the existing early implementation, it matches hardware.
|
||||
// this means fixed frequency is fully implemented and working.
|
||||
//
|
||||
// There are also several mystery fields in the operators which I have no
|
||||
// clue about: "fine" (4 bits), "eg_shift" (2 bits), and "rev" (3 bits).
|
||||
// eg_shift is some kind of envelope generator effect, but how it works is
|
||||
// unknown.
|
||||
// note by tildearrow:
|
||||
// - behavior of "fine" is now confirmed and matches hardware.
|
||||
//
|
||||
// Also, according to the site above, the panning controls are changed from
|
||||
// OPM, with a "mono" bit and only one control bit for the right channel.
|
||||
// Current implementation is just a guess.
|
||||
//
|
||||
// additional modifications by tildearrow for Furnace
|
||||
//
|
||||
|
||||
namespace ymfm
|
||||
{
|
||||
|
|
@ -409,9 +417,6 @@ uint32_t opz_registers::lfo_am_offset(uint32_t choffs) const
|
|||
|
||||
void opz_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache)
|
||||
{
|
||||
// TODO: how does fixed frequency mode work? appears to be enabled by
|
||||
// op_fix_mode(), and controlled by op_fix_range(), op_fix_frequency()
|
||||
|
||||
// TODO: what is op_rev()?
|
||||
|
||||
// set up the easy stuff
|
||||
|
|
@ -467,8 +472,8 @@ void opz_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata
|
|||
if (reverb != 0)
|
||||
cache.eg_rate[EG_REVERB] = std::min<uint32_t>(effective_rate(reverb * 4 + 2, ksrval), cache.eg_rate[EG_REVERB]);
|
||||
|
||||
// set the envelope shift; TX81Z manual says operator 1 shift is fixed at "off"
|
||||
cache.eg_shift = ((opoffs & 0x18) == 0) ? 0 : op_eg_shift(opoffs);
|
||||
// set the envelope shift; TX81Z manual says operator 1 (actually operator 4) shift is fixed at "off"
|
||||
cache.eg_shift = ((opoffs & 0x18) == 0x18) ? 0 : op_eg_shift(opoffs);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -919,12 +919,13 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_FM_FIXFREQ: {
|
||||
if (c.value<0 || c.value>3) break;
|
||||
printf("fixfreq %x\n",c.value2);
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.egt=(c.value2>0);
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6));
|
||||
if (op.egt) {
|
||||
rWrite(baseAddr+ADDR_MULT_DT,((c.value2>>4)&15)|((c.value2>>8)&7));
|
||||
rWrite(baseAddr+ADDR_MULT_DT,((c.value2>>4)&15)|(((c.value2>>8)&7)<<4));
|
||||
rWrite(baseAddr+ADDR_WS_FINE,(c.value2&15)|(op.ws<<4));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4));
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ std::vector<DivInstrumentType>& DivEngine::getPossibleInsTypes() {
|
|||
}
|
||||
|
||||
// TODO: rewrite this function (again). it's an unreliable mess.
|
||||
const char* DivEngine::getSongSystemName() {
|
||||
String DivEngine::getSongSystemName(bool isMultiSystemAcceptable) {
|
||||
switch (song.systemLen) {
|
||||
case 0:
|
||||
return "help! what's going on!";
|
||||
|
|
@ -198,7 +198,15 @@ const char* DivEngine::getSongSystemName() {
|
|||
}
|
||||
break;
|
||||
}
|
||||
return "multi-system";
|
||||
if (isMultiSystemAcceptable) return "multi-system";
|
||||
|
||||
String ret="";
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
if (i>0) ret+=" + ";
|
||||
ret+=getSystemName(song.system[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char* DivEngine::getSystemName(DivSystem sys) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue