Esempio n. 1
0
void RareSnesScanner::SearchForRareSnesFromARAM (RawFile* file)
{
	RareSnesVersion version = NONE;
	UINT ofsSongLoadASM;
	UINT ofsVCmdExecASM;
	UINT addrSeqHeader;
	UINT addrVCmdTable;
	wstring name = RawFile::removeExtFromPath(file->GetFileName());

	if (file->SearchBytePattern(ptnSongLoadDKC2, ofsSongLoadASM))
	{
		addrSeqHeader = file->GetShort(file->GetByte(ofsSongLoadASM + 8));
	}
	else if (file->SearchBytePattern(ptnSongLoadDKC, ofsSongLoadASM) &&
		file->GetShort(ofsSongLoadASM + 13) == file->GetShort(ofsSongLoadASM + 8) + 1)
	{
		addrSeqHeader = file->GetShort(ofsSongLoadASM + 8);
	}
	else
	{
		return;
	}

	if (file->SearchBytePattern(ptnVCmdExecDKC2, ofsVCmdExecASM))
	{
		addrVCmdTable = file->GetShort(ofsVCmdExecASM + 10);
		if (file->GetShort(addrVCmdTable + (0x0c * 2)) != 0)
		{
			if (file->GetShort(addrVCmdTable + (0x11 * 2)) != 0)
			{
				version = WNRN;
			}
			else
			{
				version = DKC2;
			}
		}
		else
		{
			version = KI;
		}
	}
	else if (file->SearchBytePattern(ptnVCmdExecDKC, ofsVCmdExecASM))
	{
		addrVCmdTable = file->GetShort(ofsVCmdExecASM + 12);
		version = DKC;
	}
	else
	{
		return;
	}

	RareSnesSeq* newSeq = new RareSnesSeq(file, version, addrSeqHeader, name);
	if (!newSeq->LoadVGMFile())
	{
		delete newSeq;
		return;
	}
}
Esempio n. 2
0
void RareSnesScanner::SearchForRareSnesFromARAM(RawFile *file) {
  RareSnesVersion version = RARESNES_NONE;
  uint32_t ofsSongLoadASM;
  uint32_t ofsVCmdExecASM;
  uint32_t addrSeqHeader;
  uint32_t addrVCmdTable;
  wstring name = file->tag.HasTitle() ? file->tag.title : RawFile::removeExtFromPath(file->GetFileName());

  // find a sequence
  if (file->SearchBytePattern(ptnSongLoadDKC2, ofsSongLoadASM)) {
    addrSeqHeader = file->GetShort(file->GetByte(ofsSongLoadASM + 8));
  }
  else if (file->SearchBytePattern(ptnSongLoadDKC, ofsSongLoadASM) &&
      file->GetShort(ofsSongLoadASM + 13) == file->GetShort(ofsSongLoadASM + 8) + 1) {
    addrSeqHeader = file->GetShort(ofsSongLoadASM + 8);
  }
  else {
    return;
  }

  // guess engine version
  if (file->SearchBytePattern(ptnVCmdExecDKC2, ofsVCmdExecASM)) {
    addrVCmdTable = file->GetShort(ofsVCmdExecASM + 10);
    if (file->GetShort(addrVCmdTable + (0x0c * 2)) != 0) {
      if (file->GetShort(addrVCmdTable + (0x11 * 2)) != 0) {
        version = RARESNES_WNRN;
      }
      else {
        version = RARESNES_DKC2;
      }
    }
    else {
      version = RARESNES_KI;
    }
  }
  else if (file->SearchBytePattern(ptnVCmdExecDKC, ofsVCmdExecASM)) {
    addrVCmdTable = file->GetShort(ofsVCmdExecASM + 12);
    version = RARESNES_DKC;
  }
  else {
    return;
  }

  // load sequence
  RareSnesSeq *newSeq = new RareSnesSeq(file, version, addrSeqHeader, name);
  if (!newSeq->LoadVGMFile()) {
    delete newSeq;
    return;
  }

  // Rare engine has a instrument # <--> SRCN # table, find it
  uint32_t ofsReadSRCNASM;
  if (!file->SearchBytePattern(ptnReadSRCNTable, ofsReadSRCNASM)) {
    return;
  }
  uint32_t addrSRCNTable = file->GetShort(ofsReadSRCNASM + 5);
  if (addrSRCNTable + 0x100 > 0x10000) {
    return;
  }

  // find DIR address
  uint32_t ofsSetDIRASM;
  if (!file->SearchBytePattern(ptnLoadDIR, ofsSetDIRASM)) {
    return;
  }
  uint32_t spcDirAddr = file->GetByte(ofsSetDIRASM + 4) << 8;

  // scan SRCN table
  RareSnesInstrSet *newInstrSet = new RareSnesInstrSet(file,
                                                       addrSRCNTable,
                                                       spcDirAddr,
                                                       newSeq->instrUnityKeyHints,
                                                       newSeq->instrPitchHints,
                                                       newSeq->instrADSRHints);
  if (!newInstrSet->LoadVGMFile()) {
    delete newInstrSet;
    return;
  }

  // get SRCN # range
  uint8_t maxSRCN = 0;
  std::vector<uint8_t> usedSRCNs;
  const std::vector<uint8_t> &availInstruments = newInstrSet->GetAvailableInstruments();
  for (std::vector<uint8_t>::const_iterator itr = availInstruments.begin(); itr != availInstruments.end(); ++itr) {
    uint8_t inst = (*itr);
    uint8_t srcn = file->GetByte(addrSRCNTable + inst);

    if (maxSRCN < srcn) {
      maxSRCN = srcn;
    }

    std::vector<uint8_t>::iterator itrSRCN = find(usedSRCNs.begin(), usedSRCNs.end(), srcn);
    if (itrSRCN == usedSRCNs.end()) {
      usedSRCNs.push_back(srcn);
    }
  }
  std::sort(usedSRCNs.begin(), usedSRCNs.end());

  // load BRR samples
  SNESSampColl *newSampColl = new SNESSampColl(RareSnesFormat::name, file, spcDirAddr, usedSRCNs);
  if (!newSampColl->LoadVGMFile()) {
    delete newSampColl;
    return;
  }
}
Esempio n. 3
0
bool RareSnesTrack::ReadEvent(void)
{
	RareSnesSeq* parentSeq = (RareSnesSeq*)this->parentSeq;
	uint32_t beginOffset = curOffset;
	if (curOffset >= 0x10000)
	{
		return false;
	}

	bool bWriteGenericEventAsTextEventTmp;
	uint8_t statusByte = GetByte(curOffset++);
	uint8_t newMidiVol, newMidiPan;
	bool bContinue = true;

	wstringstream desc;

	if (statusByte >= 0x80)
	{
		uint8_t noteByte = statusByte;

		// check for "reuse last key"
		if (noteByte == 0xe1)
		{
			noteByte = altNoteByte2;
		}
		else if (noteByte >= 0xe0)
		{
			noteByte = altNoteByte1;
		}

		uint8_t key = noteByte - 0x81;
		uint8_t spcKey = min(max(noteByte - 0x80 + 36 + spcTranspose, 0), 0x7f);

		uint16_t dur;
		if (defNoteDur != 0)
		{
			dur = defNoteDur;
		}
		else
		{
			if (useLongDur)
			{
				dur = GetShortBE(curOffset);
				curOffset += 2;
			}
			else
			{
				dur = GetByte(curOffset++);
			}
		}

		if (noteByte == 0x80)
		{
			//wostringstream ssTrace;
			//ssTrace << L"Rest: " << dur << L" " << defNoteDur << L" " << (useLongDur ? L"L" : L"S") << std::endl;
			//OutputDebugString(ssTrace.str().c_str());

			AddRest(beginOffset, curOffset-beginOffset, dur);
		}
		else
		{
			// a note, add hints for instrument
			if (parentSeq->instrUnityKeyHints.find(spcInstr) == parentSeq->instrUnityKeyHints.end())
			{
				parentSeq->instrUnityKeyHints[spcInstr] = spcTransposeAbs;
				parentSeq->instrPitchHints[spcInstr] = (int16_t) round(GetTuningInSemitones(spcTuning) * 100.0);
			}
			if (parentSeq->instrADSRHints.find(spcInstr) == parentSeq->instrADSRHints.end())
			{
				parentSeq->instrADSRHints[spcInstr] = spcADSR;
			}

			spcNotePitch = RareSnesSeq::NOTE_PITCH_TABLE[spcKey];
			spcNotePitch = (spcNotePitch * (1024 + spcTuning) + (spcTuning < 0 ? 1023 : 0)) / 1024;

			//wostringstream ssTrace;
			//ssTrace << L"Note: " << key << L" " << dur << L" " << defNoteDur << L" " << (useLongDur ? L"L" : L"S") << L" P=" << spcNotePitch << std::endl;
			//OutputDebugString(ssTrace.str().c_str());

			uint8_t vel = 127;
			AddNoteByDur(beginOffset, curOffset-beginOffset, key, vel, dur);
			AddTime(dur);
		}
	}
	else
	{
		RareSnesSeqEventType eventType = (RareSnesSeqEventType)0;
		map<uint8_t, RareSnesSeqEventType>::iterator pEventType = parentSeq->EventMap.find(statusByte);
		if (pEventType != parentSeq->EventMap.end())
		{
			eventType = pEventType->second;
		}

		switch (eventType)
		{
		case EVENT_UNKNOWN0:
			desc << L"Event: 0x" << std::hex << std::setfill(L'0') << std::setw(2) << std::uppercase << (int)statusByte;
			EVENT_WITH_MIDITEXT_START
			AddUnknown(beginOffset, curOffset-beginOffset, L"Unknown Event", desc.str().c_str());
			EVENT_WITH_MIDITEXT_END
			break;

		case EVENT_UNKNOWN1:
		{
			uint8_t arg1 = GetByte(curOffset++);
			desc << L"Event: 0x" << std::hex << std::setfill(L'0') << std::setw(2) << std::uppercase << (int)statusByte
				<< std::dec << std::setfill(L' ') << std::setw(0)
				<< L"  Arg1: " << (int)arg1;
			EVENT_WITH_MIDITEXT_START
			AddUnknown(beginOffset, curOffset-beginOffset, L"Unknown Event", desc.str().c_str());
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_UNKNOWN2:
		{
			uint8_t arg1 = GetByte(curOffset++);
			uint8_t arg2 = GetByte(curOffset++);
			desc << L"Event: 0x" << std::hex << std::setfill(L'0') << std::setw(2) << std::uppercase << (int)statusByte
				<< std::dec << std::setfill(L' ') << std::setw(0)
				<< L"  Arg1: " << (int)arg1
				<< L"  Arg2: " << (int)arg2;
			EVENT_WITH_MIDITEXT_START
			AddUnknown(beginOffset, curOffset-beginOffset, L"Unknown Event", desc.str().c_str());
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_UNKNOWN3:
		{
			uint8_t arg1 = GetByte(curOffset++);
			uint8_t arg2 = GetByte(curOffset++);
			uint8_t arg3 = GetByte(curOffset++);
			desc << L"Event: 0x" << std::hex << std::setfill(L'0') << std::setw(2) << std::uppercase << (int)statusByte
				<< std::dec << std::setfill(L' ') << std::setw(0)
				<< L"  Arg1: " << (int)arg1
				<< L"  Arg2: " << (int)arg2
				<< L"  Arg3: " << (int)arg3;
			EVENT_WITH_MIDITEXT_START
			AddUnknown(beginOffset, curOffset-beginOffset, L"Unknown Event", desc.str().c_str());
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_UNKNOWN4:
		{
			uint8_t arg1 = GetByte(curOffset++);
			uint8_t arg2 = GetByte(curOffset++);
			uint8_t arg3 = GetByte(curOffset++);
			uint8_t arg4 = GetByte(curOffset++);
			desc << L"Event: 0x" << std::hex << std::setfill(L'0') << std::setw(2) << std::uppercase << (int)statusByte
				<< std::dec << std::setfill(L' ') << std::setw(0)
				<< L"  Arg1: " << (int)arg1
				<< L"  Arg2: " << (int)arg2
				<< L"  Arg3: " << (int)arg3
				<< L"  Arg4: " << (int)arg4;
			EVENT_WITH_MIDITEXT_START
			AddUnknown(beginOffset, curOffset-beginOffset, L"Unknown Event", desc.str().c_str());
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_END:
			AddEndOfTrack(beginOffset, curOffset-beginOffset);
			bContinue = false;
			//loaded = true;
			break;

		case EVENT_PROGCHANGE:
		{
			uint8_t newProg = GetByte(curOffset++);
			spcInstr = newProg;
			AddProgramChange(beginOffset, curOffset-beginOffset, newProg, true);
			break;
		}

		case EVENT_PROGCHANGEVOL:
		{
			uint8_t newProg = GetByte(curOffset++);
			int8_t newVolL = (int8_t) GetByte(curOffset++);
			int8_t newVolR = (int8_t) GetByte(curOffset++);

			spcInstr = newProg;
			spcVolL = newVolL;
			spcVolR = newVolR;
			AddProgramChange(beginOffset, curOffset-beginOffset, newProg, true, L"Program Change, Volume");
			AddVolLRNoItem(spcVolL, spcVolR);
			break;
		}

		case EVENT_VOLLR:
		{
			int8_t newVolL = (int8_t) GetByte(curOffset++);
			int8_t newVolR = (int8_t) GetByte(curOffset++);

			spcVolL = newVolL;
			spcVolR = newVolR;
			AddVolLR(beginOffset, curOffset-beginOffset, spcVolL, spcVolR, L"Volume L/R");
			break;
		}

		case EVENT_VOLCENTER:
		{
			int8_t newVol = (int8_t) GetByte(curOffset++);

			spcVolL = newVol;
			spcVolR = newVol;
			AddVolLR(beginOffset, curOffset-beginOffset, spcVolL, spcVolR, L"Volume");
			break;
		}

		case EVENT_GOTO:
		{
			uint16_t dest = GetShort(curOffset); curOffset += 2;
			desc << L"Destination: $" << std::hex << std::setfill(L'0') << std::setw(4) << std::uppercase << (int)dest;
			uint32_t length = curOffset - beginOffset;

			curOffset = dest;
			if (!IsOffsetUsed(dest) || rptNestLevel != 0) // nest level check is required for Stickerbrush Symphony
				AddGenericEvent(beginOffset, length, L"Jump", desc.str().c_str(), CLR_LOOPFOREVER);
			else
				AddLoopForever(beginOffset, length, L"Jump");
			break;
		}

		case EVENT_CALLNTIMES:
		{
			uint8_t times = GetByte(curOffset++);
			uint16_t dest = GetShort(curOffset); curOffset += 2;

			desc << L"Times: " << (int)times << L"  Destination: $" << std::hex << std::setfill(L'0') << std::setw(4) << std::uppercase << (int)dest;
			AddGenericEvent(beginOffset, curOffset-beginOffset, (times == 1 ? L"Pattern Play" : L"Pattern Repeat"), desc.str().c_str(), CLR_LOOP, ICON_STARTREP);

			if (rptNestLevel == RARESNES_RPTNESTMAX)
			{
				pRoot->AddLogItem(new LogItem(L"Subroutine nest level overflow\n", LOG_LEVEL_ERR, L"RareSnesSeq"));
				bContinue = false;
				break;
			}

			rptRetnAddr[rptNestLevel] = curOffset;
			rptCount[rptNestLevel] = times;
			rptStart[rptNestLevel] = dest;
			rptNestLevel++;
			curOffset = dest;
			break;
		}

		case EVENT_CALLONCE:
		{
			uint16_t dest = GetShort(curOffset); curOffset += 2;

			desc << L"Destination: $" << std::hex << std::setfill(L'0') << std::setw(4) << std::uppercase << (int)dest;
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Pattern Play", desc.str().c_str(), CLR_LOOP, ICON_STARTREP);

			if (rptNestLevel == RARESNES_RPTNESTMAX)
			{
				pRoot->AddLogItem(new LogItem(L"Subroutine nest level overflow\n", LOG_LEVEL_ERR, L"RareSnesSeq"));
				bContinue = false;
				break;
			}

			rptRetnAddr[rptNestLevel] = curOffset;
			rptCount[rptNestLevel] = 1;
			rptStart[rptNestLevel] = dest;
			rptNestLevel++;
			curOffset = dest;
			break;
		}

		case EVENT_RET:
		{
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"End Pattern", desc.str().c_str(), CLR_TRACKEND, ICON_ENDREP);

			if (rptNestLevel == 0)
			{
				pRoot->AddLogItem(new LogItem(L"Subroutine nest level overflow\n", LOG_LEVEL_ERR, L"RareSnesSeq"));
				bContinue = false;
				break;
			}

			rptNestLevel--;
			rptCount[rptNestLevel] = (rptCount[rptNestLevel] - 1) & 0xff;
			if (rptCount[rptNestLevel] != 0) {
				// continue
				curOffset = rptStart[rptNestLevel];
				rptNestLevel++;
			}
			else {
				// return
				curOffset = rptRetnAddr[rptNestLevel];
			}
			break;
		}

		case EVENT_DEFDURON:
		{
			if (useLongDur)
			{
				defNoteDur = GetShortBE(curOffset);
				curOffset += 2;
			}
			else
			{
				defNoteDur = GetByte(curOffset++);
			}

			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Default Duration On", desc.str().c_str(), CLR_DURNOTE, ICON_NOTE);
			break;
		}

		case EVENT_DEFDUROFF:
			defNoteDur = 0;
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Default Duration Off", desc.str().c_str(), CLR_DURNOTE, ICON_NOTE);
			break;

		case EVENT_PITCHSLIDEUP:
		{
			curOffset += 5;
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Pitch Slide Up", desc.str().c_str(), CLR_PITCHBEND, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_PITCHSLIDEDOWN:
		{
			curOffset += 5;
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Pitch Slide Down", desc.str().c_str(), CLR_PITCHBEND, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_PITCHSLIDEOFF:
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Pitch Slide Off", desc.str().c_str(), CLR_PITCHBEND, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;

		case EVENT_TEMPO:
		{
			uint8_t newTempo = GetByte(curOffset++);
			parentSeq->tempo = newTempo;
			AddTempoBPM(beginOffset, curOffset-beginOffset, parentSeq->GetTempoInBPM());
			break;
		}

		case EVENT_TEMPOADD:
		{
			int8_t deltaTempo = (int8_t) GetByte(curOffset++);
			parentSeq->tempo = (parentSeq->tempo + deltaTempo) & 0xff;
			AddTempoBPM(beginOffset, curOffset-beginOffset, parentSeq->GetTempoInBPM(), L"Tempo Add");
			break;
		}

		case EVENT_VIBRATOSHORT:
		{
			curOffset += 3;
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Vibrato (Short)", desc.str().c_str(), CLR_MODULATION, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_VIBRATOOFF:
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Vibrato Off", desc.str().c_str(), CLR_MODULATION, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;

		case EVENT_VIBRATO:
		{
			curOffset += 4;
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Vibrato", desc.str().c_str(), CLR_MODULATION, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_TREMOLOOFF:
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Tremolo Off", desc.str().c_str(), CLR_MODULATION, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;

		case EVENT_TREMOLO:
		{
			curOffset += 4;
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Tremolo", desc.str().c_str(), CLR_MODULATION, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_ADSR:
		{
			uint16_t newADSR = GetShortBE(curOffset); curOffset += 2;
			spcADSR = newADSR;

			desc << L"ADSR: " << std::hex << std::setfill(L'0') << std::setw(4) << std::uppercase << (int)newADSR;
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"ADSR", desc.str().c_str(), CLR_ADSR, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_MASTVOL:
		{
			uint8_t newVol = GetByte(curOffset++);
			AddMasterVol(beginOffset, curOffset-beginOffset, newVol & 0x7f);
			break;
		}

		case EVENT_MASTVOLLR:
		{
			int8_t newVolL = (int8_t) GetByte(curOffset++);
			int8_t newVolR = (int8_t) GetByte(curOffset++);
			int8_t newVol = min(abs(newVolL) + abs(newVolR), 255) / 2; // workaround: convert to mono
			AddMasterVol(beginOffset, curOffset-beginOffset, newVol, L"Master Volume L/R");
			break;
		}

		case EVENT_TUNING:
		{
			int8_t newTuning = (int8_t) GetByte(curOffset++);
			spcTuning = newTuning;
			desc << L"Tuning: " << (int)newTuning << L" (" << (int)(GetTuningInSemitones(newTuning) * 100 + 0.5) << L" cents)";
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Tuning", desc.str().c_str(), CLR_PITCHBEND, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_TRANSPABS: // should be used for pitch correction of instrument
		{
			int8_t newTransp = (int8_t) GetByte(curOffset++);
			spcTranspose = spcTransposeAbs = newTransp;
			//AddTranspose(beginOffset, curOffset-beginOffset, 0, L"Transpose (Abs)");

			// add event without MIDI event
			if (readMode == READMODE_ADD_TO_UI && !IsOffsetUsed(beginOffset))
				AddEvent(new TransposeSeqEvent(this, newTransp, beginOffset, curOffset-beginOffset, L"Transpose (Abs)"));

			cKeyCorrection = SEQ_KEYOFS;
			break;
		}

		case EVENT_TRANSPREL:
		{
			int8_t deltaTransp = (int8_t) GetByte(curOffset++);
			spcTranspose = (spcTranspose + deltaTransp) & 0xff;
			//AddTranspose(beginOffset, curOffset-beginOffset, spcTransposeAbs - spcTranspose, L"Transpose (Rel)");

			// add event without MIDI event
			if (readMode == READMODE_ADD_TO_UI && !IsOffsetUsed(beginOffset))
				AddEvent(new TransposeSeqEvent(this, deltaTransp, beginOffset, curOffset-beginOffset, L"Transpose (Rel)"));

			cKeyCorrection += deltaTransp;
			break;
		}

		case EVENT_ECHOPARAM:
		{
			uint8_t newFeedback = GetByte(curOffset++);
			int8_t newVolL = (int8_t) GetByte(curOffset++);
			int8_t newVolR = (int8_t) GetByte(curOffset++);
			parentSeq->midiReverb = min(abs(newVolL) + abs(newVolR), 255) / 2;
			// TODO: update MIDI reverb value for each tracks?

			desc << L"Feedback: " << (int)newFeedback << L"  Volume: " << (int)newVolL << L", " << (int)newVolR;
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Echo Param", desc.str().c_str(), CLR_REVERB, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_ECHOON:
			EVENT_WITH_MIDITEXT_START
			AddReverb(beginOffset, curOffset-beginOffset, parentSeq->midiReverb, L"Echo On");
			EVENT_WITH_MIDITEXT_END
			break;

		case EVENT_ECHOOFF:
			EVENT_WITH_MIDITEXT_START
			AddReverb(beginOffset, curOffset-beginOffset, 0, L"Echo Off");
			EVENT_WITH_MIDITEXT_END
			break;

		case EVENT_ECHOFIR:
		{
			uint8_t newFIR[8];
			GetBytes(curOffset, 8, newFIR);
			curOffset += 8;

			desc << L"Filter: ";
			for (int iFIRIndex = 0; iFIRIndex < 8; iFIRIndex++)
			{
				if (iFIRIndex != 0)
					desc << L" ";
				desc << std::hex << std::setfill(L'0') << std::setw(2) << std::uppercase << (int)newFIR[iFIRIndex];
			}

			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Echo FIR", desc.str().c_str(), CLR_REVERB, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_NOISECLK:
		{
			uint8_t newCLK = GetByte(curOffset++);
			desc << L"CLK: " << (int)newCLK;
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Noise Frequency", desc.str().c_str(), CLR_CHANGESTATE, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_NOISEON:
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Noise On", desc.str().c_str(), CLR_CHANGESTATE, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;

		case EVENT_NOISEOFF:
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Noise Off", desc.str().c_str(), CLR_CHANGESTATE, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;

		case EVENT_SETALTNOTE1:
			altNoteByte1 = GetByte(curOffset++);
			desc << L"Note: " << std::hex << std::setfill(L'0') << std::setw(2) << std::uppercase << (int)altNoteByte1;
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Set Alt Note 1", desc.str().c_str(), CLR_CHANGESTATE, ICON_NOTE);
			break;

		case EVENT_SETALTNOTE2:
			altNoteByte2 = GetByte(curOffset++);
			desc << L"Note: " << std::hex << std::setfill(L'0') << std::setw(2) << std::uppercase << (int)altNoteByte2;
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Set Alt Note 2", desc.str().c_str(), CLR_CHANGESTATE, ICON_NOTE);
			break;

		case EVENT_PITCHSLIDEDOWNSHORT:
		{
			curOffset += 4;
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Pitch Slide Down (Short)", desc.str().c_str(), CLR_PITCHBEND, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_PITCHSLIDEUPSHORT:
		{
			curOffset += 4;
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Pitch Slide Up (Short)", desc.str().c_str(), CLR_PITCHBEND, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_LONGDURON:
			useLongDur = true;
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Long Duration On", desc.str().c_str(), CLR_DURNOTE, ICON_NOTE);
			break;

		case EVENT_LONGDUROFF:
			useLongDur = false;
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Long Duration Off", desc.str().c_str(), CLR_DURNOTE, ICON_NOTE);
			break;

		case EVENT_SETVOLADSRPRESET1:
		{
			int8_t newVolL = (int8_t) GetByte(curOffset++);
			int8_t newVolR = (int8_t) GetByte(curOffset++);
			uint16_t newADSR = GetShortBE(curOffset); curOffset += 2;

			parentSeq->presetVolL[0] = newVolL;
			parentSeq->presetVolR[0] = newVolR;
			parentSeq->presetADSR[0] = newADSR;

			// add event without MIDI events
			CalcVolPanFromVolLR(spcVolL, spcVolR, newMidiVol, newMidiPan);
			if (readMode == READMODE_ADD_TO_UI && !IsOffsetUsed(beginOffset))
				AddEvent(new VolSeqEvent(this, newMidiVol, beginOffset, curOffset-beginOffset, L"Set Vol/ADSR Preset 1"));
			break;
		}

		case EVENT_SETVOLADSRPRESET2:
		{
			int8_t newVolL = (int8_t) GetByte(curOffset++);
			int8_t newVolR = (int8_t) GetByte(curOffset++);
			uint16_t newADSR = GetShortBE(curOffset); curOffset += 2;

			parentSeq->presetVolL[1] = newVolL;
			parentSeq->presetVolR[1] = newVolR;
			parentSeq->presetADSR[1] = newADSR;

			// add event without MIDI events
			CalcVolPanFromVolLR(spcVolL, spcVolR, newMidiVol, newMidiPan);
			if (readMode == READMODE_ADD_TO_UI && !IsOffsetUsed(beginOffset))
				AddEvent(new VolSeqEvent(this, newMidiVol, beginOffset, curOffset-beginOffset, L"Set Vol/ADSR Preset 2"));
			break;
		}

		case EVENT_SETVOLADSRPRESET3:
		{
			int8_t newVolL = (int8_t) GetByte(curOffset++);
			int8_t newVolR = (int8_t) GetByte(curOffset++);
			uint16_t newADSR = GetShortBE(curOffset); curOffset += 2;

			parentSeq->presetVolL[2] = newVolL;
			parentSeq->presetVolR[2] = newVolR;
			parentSeq->presetADSR[2] = newADSR;

			// add event without MIDI events
			CalcVolPanFromVolLR(spcVolL, spcVolR, newMidiVol, newMidiPan);
			if (readMode == READMODE_ADD_TO_UI && !IsOffsetUsed(beginOffset))
				AddEvent(new VolSeqEvent(this, newMidiVol, beginOffset, curOffset-beginOffset, L"Set Vol/ADSR Preset 3"));
			break;
		}

		case EVENT_SETVOLADSRPRESET4:
		{
			int8_t newVolL = (int8_t) GetByte(curOffset++);
			int8_t newVolR = (int8_t) GetByte(curOffset++);
			uint16_t newADSR = GetShortBE(curOffset); curOffset += 2;

			parentSeq->presetVolL[3] = newVolL;
			parentSeq->presetVolR[3] = newVolR;
			parentSeq->presetADSR[3] = newADSR;

			// add event without MIDI events
			CalcVolPanFromVolLR(spcVolL, spcVolR, newMidiVol, newMidiPan);
			if (readMode == READMODE_ADD_TO_UI && !IsOffsetUsed(beginOffset))
				AddEvent(new VolSeqEvent(this, newMidiVol, beginOffset, curOffset-beginOffset, L"Set Vol/ADSR Preset 4"));
			break;
		}

		case EVENT_SETVOLADSRPRESET5:
		{
			int8_t newVolL = (int8_t) GetByte(curOffset++);
			int8_t newVolR = (int8_t) GetByte(curOffset++);
			uint16_t newADSR = GetShortBE(curOffset); curOffset += 2;

			parentSeq->presetVolL[4] = newVolL;
			parentSeq->presetVolR[4] = newVolR;
			parentSeq->presetADSR[4] = newADSR;

			// add event without MIDI events
			CalcVolPanFromVolLR(spcVolL, spcVolR, newMidiVol, newMidiPan);
			if (readMode == READMODE_ADD_TO_UI && !IsOffsetUsed(beginOffset))
				AddEvent(new VolSeqEvent(this, newMidiVol, beginOffset, curOffset-beginOffset, L"Set Vol/ADSR Preset 5"));
			break;
		}

		case EVENT_GETVOLADSRPRESET1:
			spcVolL = parentSeq->presetVolL[0];
			spcVolR = parentSeq->presetVolR[0];
			EVENT_WITH_MIDITEXT_START
			AddVolLR(beginOffset, curOffset-beginOffset, spcVolL, spcVolR, L"Get Vol/ADSR Preset 1");
			EVENT_WITH_MIDITEXT_END
			break;

		case EVENT_GETVOLADSRPRESET2:
			spcVolL = parentSeq->presetVolL[1];
			spcVolR = parentSeq->presetVolR[1];
			EVENT_WITH_MIDITEXT_START
			AddVolLR(beginOffset, curOffset-beginOffset, spcVolL, spcVolR, L"Get Vol/ADSR Preset 2");
			EVENT_WITH_MIDITEXT_END
			break;

		case EVENT_GETVOLADSRPRESET3:
			spcVolL = parentSeq->presetVolL[2];
			spcVolR = parentSeq->presetVolR[2];
			EVENT_WITH_MIDITEXT_START
			AddVolLR(beginOffset, curOffset-beginOffset, spcVolL, spcVolR, L"Get Vol/ADSR Preset 3");
			EVENT_WITH_MIDITEXT_END
			break;

		case EVENT_GETVOLADSRPRESET4:
			spcVolL = parentSeq->presetVolL[3];
			spcVolR = parentSeq->presetVolR[3];
			EVENT_WITH_MIDITEXT_START
			AddVolLR(beginOffset, curOffset-beginOffset, spcVolL, spcVolR, L"Get Vol/ADSR Preset 4");
			EVENT_WITH_MIDITEXT_END
			break;

		case EVENT_GETVOLADSRPRESET5:
			spcVolL = parentSeq->presetVolL[4];
			spcVolR = parentSeq->presetVolR[4];
			EVENT_WITH_MIDITEXT_START
			AddVolLR(beginOffset, curOffset-beginOffset, spcVolL, spcVolR, L"Get Vol/ADSR Preset 5");
			EVENT_WITH_MIDITEXT_END
			break;

		case EVENT_TIMERFREQ:
		{
			uint8_t newFreq = GetByte(curOffset++);
			parentSeq->timerFreq = newFreq;
			AddTempoBPM(beginOffset, curOffset-beginOffset, parentSeq->GetTempoInBPM(), L"Timer Frequency");
			break;
		}

		//case EVENT_CONDJUMP:
		//	break;

		//case EVENT_SETCONDJUMPPARAM:
		//	break;

		case EVENT_RESETADSR:
			spcADSR = 0x8FE0;
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Reset ADSR", L"ADSR: 8FE0", CLR_ADSR, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;

		case EVENT_RESETADSRSOFT:
			spcADSR = 0x8EE0;
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Reset ADSR (Soft)", L"ADSR: 8EE0", CLR_ADSR, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;

		case EVENT_VOICEPARAMSHORT:
		{
			uint8_t newProg = GetByte(curOffset++);
			int8_t newTransp = (int8_t) GetByte(curOffset++);
			int8_t newTuning = (int8_t) GetByte(curOffset++);

			desc << L"Program Number: " << (int)newProg << L"  Transpose: " << (int)newTransp << L"  Tuning: " << (int)newTuning << L" (" << (int)(GetTuningInSemitones(newTuning) * 100 + 0.5) << L" cents)";;

			// instrument
			spcInstr = newProg;
			AddProgramChange(beginOffset, curOffset-beginOffset, newProg, true, L"Program Change, Transpose, Tuning");

			// transpose
			spcTranspose = spcTransposeAbs = newTransp;
			cKeyCorrection = SEQ_KEYOFS;

			// tuning
			spcTuning = newTuning;
			break;
		}

		case EVENT_VOICEPARAM:
		{
			uint8_t newProg = GetByte(curOffset++);
			int8_t newTransp = (int8_t) GetByte(curOffset++);
			int8_t newTuning = (int8_t) GetByte(curOffset++);
			int8_t newVolL = (int8_t) GetByte(curOffset++);
			int8_t newVolR = (int8_t) GetByte(curOffset++);
			uint16_t newADSR = GetShortBE(curOffset); curOffset += 2;

			desc << L"Program Number: " << (int)newProg << L"  Transpose: " << (int)newTransp << L"  Tuning: " << (int)newTuning << L" (" << (int)(GetTuningInSemitones(newTuning) * 100 + 0.5) << L" cents)";;
			desc << L"  Volume: " << (int)newVolL << L", " << (int)newVolR;
			desc << L"  ADSR: " << std::hex << std::setfill(L'0') << std::setw(4) << std::uppercase << (int)newADSR;

			// instrument
			spcInstr = newProg;
			AddProgramChange(beginOffset, curOffset-beginOffset, newProg, true, L"Program Change, Transpose, Tuning, Volume L/R, ADSR");

			// transpose
			spcTranspose = spcTransposeAbs = newTransp;
			cKeyCorrection = SEQ_KEYOFS;

			// tuning
			spcTuning = newTuning;

			// volume
			spcVolL = newVolL;
			spcVolR = newVolR;
			AddVolLRNoItem(spcVolL, spcVolR);

			// ADSR
			spcADSR = newADSR;

			break;
		}

		case EVENT_ECHODELAY:
		{
			uint8_t newEDL = GetByte(curOffset++);
			desc << L"Delay: " << (int)newEDL;
			EVENT_WITH_MIDITEXT_START
			AddGenericEvent(beginOffset, curOffset-beginOffset, L"Echo Delay", desc.str().c_str(), CLR_REVERB, ICON_CONTROL);
			EVENT_WITH_MIDITEXT_END
			break;
		}

		case EVENT_SETVOLPRESETS:
		{
			int8_t newVolL1 = (int8_t) GetByte(curOffset++);
			int8_t newVolR1 = (int8_t) GetByte(curOffset++);
			int8_t newVolL2 = (int8_t) GetByte(curOffset++);
			int8_t newVolR2 = (int8_t) GetByte(curOffset++);

			parentSeq->presetVolL[0] = newVolL1;
			parentSeq->presetVolR[0] = newVolR1;
			parentSeq->presetVolL[1] = newVolL2;
			parentSeq->presetVolR[1] = newVolR2;

			// add event without MIDI events
			CalcVolPanFromVolLR(parentSeq->presetVolL[0], parentSeq->presetVolR[0], newMidiVol, newMidiPan);
			if (readMode == READMODE_ADD_TO_UI && !IsOffsetUsed(beginOffset))
				AddEvent(new VolSeqEvent(this, newMidiVol, beginOffset, curOffset-beginOffset, L"Set Volume Preset"));
			break;
		}

		case EVENT_GETVOLPRESET1:
			spcVolL = parentSeq->presetVolL[0];
			spcVolR = parentSeq->presetVolR[0];
			AddVolLR(beginOffset, curOffset-beginOffset, spcVolL, spcVolR, L"Get Volume Preset 1");
			break;

		case EVENT_GETVOLPRESET2:
			spcVolL = parentSeq->presetVolL[1];
			spcVolR = parentSeq->presetVolR[1];
			AddVolLR(beginOffset, curOffset-beginOffset, spcVolL, spcVolR, L"Get Volume Preset 2");
			break;

		case EVENT_LFOOFF:
			if (readMode == READMODE_ADD_TO_UI && !IsOffsetUsed(beginOffset))
				AddEvent(new ModulationSeqEvent(this, 0, beginOffset, curOffset-beginOffset, L"Pitch Slide/Vibrato/Tremolo Off"));
			break;

		default:
			desc << L"Event: 0x" << std::hex << std::setfill(L'0') << std::setw(2) << std::uppercase << (int)statusByte;
			EVENT_WITH_MIDITEXT_START
			AddUnknown(beginOffset, curOffset-beginOffset, L"Unknown Event", desc.str().c_str());
			EVENT_WITH_MIDITEXT_END
			pRoot->AddLogItem(new LogItem(wstring(L"Unknown Event - ") + desc.str(), LOG_LEVEL_ERR, wstring(L"RareSnesSeq")));
			bContinue = false;
			break;
		}
	}

	//wostringstream ssTrace;
	//ssTrace << L"" << std::hex << std::setfill(L'0') << std::setw(8) << std::uppercase << beginOffset << L": " << std::setw(2) << (int)statusByte  << L" -> " << std::setw(8) << curOffset << std::endl;
	//OutputDebugString(ssTrace.str().c_str());

	return bContinue;
}