2022-02-18 12:58:36 -05:00
/**
* Furnace Tracker - multi - system chiptune tracker
2025-01-28 18:49:19 -05:00
* Copyright ( C ) 2021 - 2025 tildearrow and contributors
2022-02-18 12:58:36 -05:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
# include "engine.h"
# include "../ta-log.h"
# include "../utfutils.h"
2022-02-27 00:41:27 -05:00
# include "song.h"
2022-02-18 12:58:36 -05:00
2024-08-23 00:17:22 -04:00
// this function is so long
// may as well make it something else
2025-06-06 03:45:59 -04:00
void DivEngine : : performVGMWrite ( SafeWriter * w , DivSystem sys , DivRegWrite & write , int streamOff , double * loopTimer , double * loopFreq , int * loopSample , bool * sampleDir , bool isSecond , int * pendingFreq , int * playingSample , int * setPos , unsigned int * sampleOff8 , unsigned int * sampleLen8 , size_t bankOffset , bool directStream , bool * sampleStoppable , bool dpcm07 , DivDispatch * * writeNES , int rateCorrection ) {
2022-03-05 04:02:01 -05:00
unsigned char baseAddr1 = isSecond ? 0xa0 : 0x50 ;
unsigned char baseAddr2 = isSecond ? 0x80 : 0 ;
unsigned short baseAddr2S = isSecond ? 0x8000 : 0 ;
unsigned char smsAddr = isSecond ? 0x30 : 0x50 ;
2023-01-27 14:52:11 -05:00
unsigned char ggAddr = isSecond ? 0x3f : 0x4f ;
2022-05-20 14:45:26 -04:00
unsigned char rf5c68Addr = isSecond ? 0xb1 : 0xb0 ;
2022-02-18 12:58:36 -05:00
if ( write . addr = = 0xffffffff ) { // Furnace fake reset
switch ( sys ) {
case DIV_SYSTEM_YM2612 :
2022-02-27 00:39:16 -05:00
case DIV_SYSTEM_YM2612_EXT :
2022-12-20 17:49:53 -05:00
case DIV_SYSTEM_YM2612_DUALPCM :
case DIV_SYSTEM_YM2612_DUALPCM_EXT :
2024-02-29 21:33:40 -05:00
case DIV_SYSTEM_YM2612_CSM :
2022-02-18 12:58:36 -05:00
for ( int i = 0 ; i < 3 ; i + + ) { // set SL and RR to highest
2022-03-05 04:02:01 -05:00
w - > writeC ( 2 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x80 + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 2 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x84 + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 2 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x88 + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 2 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x8c + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 3 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x80 + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 3 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x84 + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 3 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x88 + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 3 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x8c + i ) ;
w - > writeC ( 0xff ) ;
}
for ( int i = 0 ; i < 3 ; i + + ) { // note off
2022-03-05 04:02:01 -05:00
w - > writeC ( 2 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x28 ) ;
w - > writeC ( i ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 2 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x28 ) ;
w - > writeC ( 4 + i ) ;
}
2022-03-05 04:02:01 -05:00
w - > writeC ( 2 | baseAddr1 ) ; // disable DAC
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x2b ) ;
w - > writeC ( 0 ) ;
break ;
case DIV_SYSTEM_SMS :
for ( int i = 0 ; i < 4 ; i + + ) {
2022-03-05 04:02:01 -05:00
w - > writeC ( smsAddr ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x90 | ( i < < 5 ) | 15 ) ;
}
break ;
2022-10-16 02:27:39 -04:00
case DIV_SYSTEM_T6W28 :
for ( int i = 0 ; i < 4 ; i + + ) {
w - > writeC ( 0x30 ) ;
w - > writeC ( 0x90 | ( i < < 5 ) | 15 ) ;
w - > writeC ( 0x50 ) ;
w - > writeC ( 0x90 | ( i < < 5 ) | 15 ) ;
}
break ;
2022-02-18 12:58:36 -05:00
case DIV_SYSTEM_GB :
// square 1
w - > writeC ( 0xb3 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 2 | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0 ) ;
w - > writeC ( 0xb3 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 4 | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x80 ) ;
// square 2
w - > writeC ( 0xb3 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 7 | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0 ) ;
w - > writeC ( 0xb3 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 9 | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x80 ) ;
// wave
w - > writeC ( 0xb3 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 0x0c | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0 ) ;
w - > writeC ( 0xb3 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 0x0e | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x80 ) ;
// noise
w - > writeC ( 0xb3 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 0x11 | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0 ) ;
w - > writeC ( 0xb3 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 0x13 | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x80 ) ;
break ;
case DIV_SYSTEM_PCE :
for ( int i = 0 ; i < 6 ; i + + ) {
w - > writeC ( 0xb9 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 0 | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( i ) ;
w - > writeC ( 0xb9 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 4 | baseAddr2 ) ;
2023-02-08 18:04:09 -05:00
w - > writeC ( 0x5f ) ;
w - > writeC ( 0xb9 ) ;
w - > writeC ( 4 | baseAddr2 ) ;
w - > writeC ( 0x1f ) ;
for ( int j = 0 ; j < 32 ; j + + ) {
w - > writeC ( 0xb9 ) ;
w - > writeC ( 6 | baseAddr2 ) ;
w - > writeC ( 0 ) ;
}
2022-02-18 12:58:36 -05:00
}
break ;
case DIV_SYSTEM_NES :
w - > writeC ( 0xb4 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 0x15 | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0 ) ;
break ;
case DIV_SYSTEM_YM2151 :
for ( int i = 0 ; i < 8 ; i + + ) {
2022-03-05 04:02:01 -05:00
w - > writeC ( 4 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0xe0 + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 4 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0xe8 + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 4 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0xf0 + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 4 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0xf8 + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 4 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x08 ) ;
w - > writeC ( i ) ;
}
2022-02-27 00:39:16 -05:00
break ;
case DIV_SYSTEM_SEGAPCM :
case DIV_SYSTEM_SEGAPCM_COMPAT :
for ( int i = 0 ; i < 16 ; i + + ) {
w - > writeC ( 0xc0 ) ;
2022-03-05 04:02:01 -05:00
w - > writeS ( ( 0x86 | baseAddr2S ) + ( i < < 3 ) ) ;
2022-02-27 00:39:16 -05:00
w - > writeC ( 3 ) ;
2022-02-18 12:58:36 -05:00
}
break ;
2022-03-06 12:31:03 -05:00
case DIV_SYSTEM_X1_010 :
for ( int i = 0 ; i < 16 ; i + + ) {
w - > writeC ( 0xc8 ) ;
2022-03-11 20:22:21 -05:00
w - > writeS_BE ( baseAddr2S + ( i < < 3 ) ) ;
2022-03-06 12:31:03 -05:00
w - > writeC ( 0 ) ;
}
break ;
2022-02-18 12:58:36 -05:00
case DIV_SYSTEM_YM2610 :
case DIV_SYSTEM_YM2610_FULL :
2022-02-24 11:02:35 -05:00
case DIV_SYSTEM_YM2610B :
2022-02-18 12:58:36 -05:00
case DIV_SYSTEM_YM2610_EXT :
case DIV_SYSTEM_YM2610_FULL_EXT :
2022-02-24 11:02:35 -05:00
case DIV_SYSTEM_YM2610B_EXT :
2025-06-14 19:08:58 -04:00
case DIV_SYSTEM_YM2610_CSM :
case DIV_SYSTEM_YM2610B_CSM :
2022-05-19 19:09:46 -04:00
// TODO: YM2610B channels 1 and 4 and ADPCM-B
2022-02-18 12:58:36 -05:00
for ( int i = 0 ; i < 2 ; i + + ) { // set SL and RR to highest
2022-03-05 04:02:01 -05:00
w - > writeC ( 8 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x81 + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 8 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x85 + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 8 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x89 + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 8 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x8d + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 9 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x81 + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 9 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x85 + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 9 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x89 + i ) ;
w - > writeC ( 0xff ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 9 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x8d + i ) ;
w - > writeC ( 0xff ) ;
}
for ( int i = 0 ; i < 2 ; i + + ) { // note off
2022-03-05 04:02:01 -05:00
w - > writeC ( 8 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x28 ) ;
w - > writeC ( 1 + i ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 8 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x28 ) ;
w - > writeC ( 5 + i ) ;
}
// reset AY
2022-03-05 04:02:01 -05:00
w - > writeC ( 8 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 7 ) ;
w - > writeC ( 0x3f ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 8 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 8 ) ;
w - > writeC ( 0 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 8 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 9 ) ;
w - > writeC ( 0 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 8 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 10 ) ;
w - > writeC ( 0 ) ;
// reset sample
2022-03-05 04:02:01 -05:00
w - > writeC ( 9 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0 ) ;
w - > writeC ( 0xbf ) ;
break ;
2022-03-02 00:58:49 -05:00
case DIV_SYSTEM_OPLL :
case DIV_SYSTEM_OPLL_DRUMS :
case DIV_SYSTEM_VRC7 :
for ( int i = 0 ; i < 9 ; i + + ) {
2022-03-05 04:02:01 -05:00
w - > writeC ( 1 | baseAddr1 ) ;
2022-03-02 00:58:49 -05:00
w - > writeC ( 0x20 + i ) ;
w - > writeC ( 0 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 1 | baseAddr1 ) ;
2022-03-02 00:58:49 -05:00
w - > writeC ( 0x30 + i ) ;
w - > writeC ( 0 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 1 | baseAddr1 ) ;
2022-03-02 00:58:49 -05:00
w - > writeC ( 0x10 + i ) ;
w - > writeC ( 0 ) ;
}
break ;
2022-12-20 17:49:53 -05:00
case DIV_SYSTEM_YM2203 :
case DIV_SYSTEM_YM2203_EXT :
2025-06-14 19:08:58 -04:00
case DIV_SYSTEM_YM2203_CSM :
2022-05-19 18:46:41 -04:00
for ( int i = 0 ; i < 3 ; i + + ) { // set SL and RR to highest
w - > writeC ( 5 | baseAddr1 ) ;
w - > writeC ( 0x80 + i ) ;
w - > writeC ( 0xff ) ;
w - > writeC ( 5 | baseAddr1 ) ;
w - > writeC ( 0x84 + i ) ;
w - > writeC ( 0xff ) ;
w - > writeC ( 5 | baseAddr1 ) ;
w - > writeC ( 0x88 + i ) ;
w - > writeC ( 0xff ) ;
w - > writeC ( 5 | baseAddr1 ) ;
w - > writeC ( 0x8c + i ) ;
w - > writeC ( 0xff ) ;
}
for ( int i = 0 ; i < 3 ; i + + ) { // note off
w - > writeC ( 5 | baseAddr1 ) ;
w - > writeC ( 0x28 ) ;
w - > writeC ( i ) ;
}
// SSG
w - > writeC ( 5 | baseAddr1 ) ;
w - > writeC ( 7 ) ;
w - > writeC ( 0x3f ) ;
w - > writeC ( 5 | baseAddr1 ) ;
w - > writeC ( 8 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 5 | baseAddr1 ) ;
w - > writeC ( 9 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 5 | baseAddr1 ) ;
w - > writeC ( 10 ) ;
w - > writeC ( 0 ) ;
break ;
2022-02-18 12:58:36 -05:00
case DIV_SYSTEM_AY8910 :
w - > writeC ( 0xa0 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 7 | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x3f ) ;
w - > writeC ( 0xa0 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 8 | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0 ) ;
w - > writeC ( 0xa0 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 9 | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0 ) ;
w - > writeC ( 0xa0 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 10 | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0 ) ;
break ;
case DIV_SYSTEM_AY8930 :
w - > writeC ( 0xa0 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 0x0d | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0 ) ;
w - > writeC ( 0xa0 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 0x0d | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0xa0 ) ;
break ;
case DIV_SYSTEM_SAA1099 :
w - > writeC ( 0xbd ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 0x1c | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x02 ) ;
w - > writeC ( 0xbd ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 0x14 | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0 ) ;
w - > writeC ( 0xbd ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( 0x15 | baseAddr2 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0 ) ;
for ( int i = 0 ; i < 6 ; i + + ) {
w - > writeC ( 0xbd ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( ( 0 | baseAddr2 ) + i ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 0 ) ;
}
break ;
2022-12-20 14:54:55 -05:00
case DIV_SYSTEM_POKEY :
for ( int i = 0 ; i < 9 ; i + + ) {
w - > writeC ( 0xbb ) ;
w - > writeC ( i | baseAddr2 ) ;
w - > writeC ( 0 ) ;
}
break ;
2022-02-20 12:15:15 -05:00
case DIV_SYSTEM_LYNX :
2023-01-15 10:27:20 -05:00
w - > writeC ( 0x40 ) ;
2022-02-21 06:41:06 -05:00
w - > writeC ( 0x44 ) ;
w - > writeC ( 0xff ) ; //stereo attenuation select
2023-01-15 10:27:20 -05:00
w - > writeC ( 0x40 ) ;
2022-02-20 12:15:15 -05:00
w - > writeC ( 0x50 ) ;
2022-02-21 06:41:06 -05:00
w - > writeC ( 0x00 ) ; //stereo channel disable
for ( int i = 0 ; i < 4 ; i + + ) { //stereo attenuation value
2023-01-15 10:27:20 -05:00
w - > writeC ( 0x40 ) ;
2022-02-21 06:41:06 -05:00
w - > writeC ( 0x40 + i ) ;
w - > writeC ( 0xff ) ;
}
2022-02-20 12:15:15 -05:00
break ;
2022-02-22 04:01:57 -05:00
case DIV_SYSTEM_QSOUND :
2022-02-22 18:21:57 -05:00
for ( int i = 0 ; i < 16 ; i + + ) {
w - > writeC ( 0xc4 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 2 + ( i * 8 ) ) ;
w - > writeC ( 0xc4 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 6 + ( i * 8 ) ) ;
}
for ( int i = 0 ; i < 3 ; i + + ) {
w - > writeC ( 0xc4 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0xcd + ( i * 4 ) ) ;
w - > writeC ( 0xc4 ) ;
w - > writeC ( 0x00 ) ;
w - > writeC ( 0x01 ) ;
w - > writeC ( 0xd6 + i ) ;
}
2022-02-22 04:01:57 -05:00
break ;
2022-04-20 12:52:37 -04:00
case DIV_SYSTEM_ES5506 :
for ( int i = 0 ; i < 32 ; i + + ) {
for ( int b = 0 ; b < 4 ; b + + ) {
w - > writeC ( 0xbe ) ;
2025-06-01 06:19:42 -04:00
w - > writeC ( baseAddr2 | ( ( 0xf < < 2 ) + b ) ) ;
w - > writeC ( ( b = = 3 ) ? i : 0 ) ;
2022-04-20 12:52:37 -04:00
}
unsigned int init_cr = 0x0303 ;
for ( int b = 0 ; b < 4 ; b + + ) {
w - > writeC ( 0xbe ) ;
2025-06-01 06:19:42 -04:00
w - > writeC ( baseAddr2 | b ) ;
2022-04-20 12:52:37 -04:00
w - > writeC ( init_cr > > ( 24 - ( b < < 3 ) ) ) ;
}
for ( int r = 1 ; r < 11 ; r + + ) {
for ( int b = 0 ; b < 4 ; b + + ) {
w - > writeC ( 0xbe ) ;
2025-06-01 06:19:42 -04:00
w - > writeC ( baseAddr2 | ( ( r < < 2 ) + b ) ) ;
2022-04-20 12:52:37 -04:00
w - > writeC ( ( ( r = = 7 | | r = = 9 ) & & b & 2 ) ? 0xff : 0 ) ;
}
}
for ( int b = 0 ; b < 4 ; b + + ) {
w - > writeC ( 0xbe ) ;
2025-06-01 06:19:42 -04:00
w - > writeC ( baseAddr2 | ( ( 0xf < < 2 ) + b ) ) ;
2022-04-20 12:52:37 -04:00
w - > writeC ( 0x20 | i ) ;
}
for ( int r = 1 ; r < 10 ; r + + ) {
for ( int b = 0 ; b < 4 ; b + + ) {
w - > writeC ( 0xbe ) ;
2025-06-01 06:19:42 -04:00
w - > writeC ( baseAddr2 | ( ( r < < 2 ) + b ) ) ;
2022-04-20 12:52:37 -04:00
w - > writeC ( 0 ) ;
}
}
}
break ;
2022-03-30 04:36:16 -04:00
case DIV_SYSTEM_OPL :
case DIV_SYSTEM_OPL_DRUMS :
2022-03-30 05:27:11 -04:00
// disable envelope
for ( int i = 0 ; i < 6 ; i + + ) {
w - > writeC ( 0x0b | baseAddr1 ) ;
w - > writeC ( 0x80 + i ) ;
w - > writeC ( 0x0f ) ;
w - > writeC ( 0x0b | baseAddr1 ) ;
w - > writeC ( 0x88 + i ) ;
w - > writeC ( 0x0f ) ;
w - > writeC ( 0x0b | baseAddr1 ) ;
w - > writeC ( 0x90 + i ) ;
w - > writeC ( 0x0f ) ;
}
// key off + freq reset
for ( int i = 0 ; i < 9 ; i + + ) {
w - > writeC ( 0x0b | baseAddr1 ) ;
w - > writeC ( 0xa0 + i ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0x0b | baseAddr1 ) ;
w - > writeC ( 0xb0 + i ) ;
w - > writeC ( 0 ) ;
}
2022-03-30 04:36:16 -04:00
break ;
2022-05-19 19:09:46 -04:00
case DIV_SYSTEM_Y8950 :
case DIV_SYSTEM_Y8950_DRUMS :
// disable envelope
for ( int i = 0 ; i < 6 ; i + + ) {
2022-05-24 16:08:01 -04:00
w - > writeC ( 0x0c | baseAddr1 ) ;
2022-05-19 19:09:46 -04:00
w - > writeC ( 0x80 + i ) ;
w - > writeC ( 0x0f ) ;
2022-05-24 16:08:01 -04:00
w - > writeC ( 0x0c | baseAddr1 ) ;
2022-05-19 19:09:46 -04:00
w - > writeC ( 0x88 + i ) ;
w - > writeC ( 0x0f ) ;
2022-05-24 16:08:01 -04:00
w - > writeC ( 0x0c | baseAddr1 ) ;
2022-05-19 19:09:46 -04:00
w - > writeC ( 0x90 + i ) ;
w - > writeC ( 0x0f ) ;
}
// key off + freq reset
for ( int i = 0 ; i < 9 ; i + + ) {
2022-05-24 16:08:01 -04:00
w - > writeC ( 0x0c | baseAddr1 ) ;
2022-05-19 19:09:46 -04:00
w - > writeC ( 0xa0 + i ) ;
w - > writeC ( 0 ) ;
2022-05-24 16:08:01 -04:00
w - > writeC ( 0x0c | baseAddr1 ) ;
2022-05-19 19:09:46 -04:00
w - > writeC ( 0xb0 + i ) ;
w - > writeC ( 0 ) ;
}
// TODO: ADPCM
break ;
2022-03-30 04:36:16 -04:00
case DIV_SYSTEM_OPL2 :
case DIV_SYSTEM_OPL2_DRUMS :
2022-03-30 05:27:11 -04:00
// disable envelope
for ( int i = 0 ; i < 6 ; i + + ) {
w - > writeC ( 0x0a | baseAddr1 ) ;
w - > writeC ( 0x80 + i ) ;
w - > writeC ( 0x0f ) ;
w - > writeC ( 0x0a | baseAddr1 ) ;
w - > writeC ( 0x88 + i ) ;
w - > writeC ( 0x0f ) ;
w - > writeC ( 0x0a | baseAddr1 ) ;
w - > writeC ( 0x90 + i ) ;
w - > writeC ( 0x0f ) ;
}
// key off + freq reset
for ( int i = 0 ; i < 9 ; i + + ) {
w - > writeC ( 0x0a | baseAddr1 ) ;
w - > writeC ( 0xa0 + i ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0x0a | baseAddr1 ) ;
w - > writeC ( 0xb0 + i ) ;
w - > writeC ( 0 ) ;
}
2022-03-30 04:36:16 -04:00
break ;
case DIV_SYSTEM_OPL3 :
case DIV_SYSTEM_OPL3_DRUMS :
2022-03-30 05:27:11 -04:00
// disable envelope
for ( int i = 0 ; i < 6 ; i + + ) {
w - > writeC ( 0x0e | baseAddr1 ) ;
w - > writeC ( 0x80 + i ) ;
w - > writeC ( 0x0f ) ;
w - > writeC ( 0x0e | baseAddr1 ) ;
w - > writeC ( 0x88 + i ) ;
w - > writeC ( 0x0f ) ;
w - > writeC ( 0x0e | baseAddr1 ) ;
w - > writeC ( 0x90 + i ) ;
w - > writeC ( 0x0f ) ;
w - > writeC ( 0x0f | baseAddr1 ) ;
w - > writeC ( 0x80 + i ) ;
w - > writeC ( 0x0f ) ;
w - > writeC ( 0x0f | baseAddr1 ) ;
w - > writeC ( 0x88 + i ) ;
w - > writeC ( 0x0f ) ;
w - > writeC ( 0x0f | baseAddr1 ) ;
w - > writeC ( 0x90 + i ) ;
w - > writeC ( 0x0f ) ;
}
// key off + freq reset
for ( int i = 0 ; i < 9 ; i + + ) {
w - > writeC ( 0x0e | baseAddr1 ) ;
w - > writeC ( 0xa0 + i ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0x0e | baseAddr1 ) ;
w - > writeC ( 0xb0 + i ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0x0f | baseAddr1 ) ;
w - > writeC ( 0xa0 + i ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0x0f | baseAddr1 ) ;
w - > writeC ( 0xb0 + i ) ;
w - > writeC ( 0 ) ;
}
// reset 4-op
w - > writeC ( 0x0f | baseAddr1 ) ;
w - > writeC ( 0x04 ) ;
w - > writeC ( 0x00 ) ;
2022-03-30 04:36:16 -04:00
break ;
2022-05-17 02:52:18 -04:00
case DIV_SYSTEM_SCC :
case DIV_SYSTEM_SCC_PLUS :
w - > writeC ( 0xd2 ) ;
w - > writeC ( baseAddr2 | 3 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0 ) ;
break ;
2022-05-20 14:45:26 -04:00
case DIV_SYSTEM_RF5C68 :
w - > writeC ( rf5c68Addr ) ;
w - > writeC ( 7 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( rf5c68Addr ) ;
w - > writeC ( 8 ) ;
w - > writeC ( 0xff ) ;
2022-05-28 23:18:38 -04:00
break ;
2023-09-11 02:19:02 -04:00
case DIV_SYSTEM_MSM6258 :
2024-11-11 04:04:04 -05:00
w - > writeC ( 0xb7 ) ; // stop
2023-09-11 02:19:02 -04:00
w - > writeC ( baseAddr2 | 0 ) ;
w - > writeC ( 1 ) ;
break ;
2022-05-28 19:15:26 -04:00
case DIV_SYSTEM_MSM6295 :
w - > writeC ( 0xb8 ) ; // disable all channels
w - > writeC ( baseAddr2 | 0 ) ;
w - > writeC ( 0x78 ) ;
w - > writeC ( 0xb8 ) ; // select rate
w - > writeC ( baseAddr2 | 12 ) ;
w - > writeC ( 1 ) ;
2022-05-28 23:18:38 -04:00
break ;
2022-10-16 02:27:39 -04:00
case DIV_SYSTEM_VBOY :
// isn't it amazing when a chip has a built-in reset command?
w - > writeC ( 0xc7 ) ;
w - > writeS_BE ( baseAddr2S | ( 0x580 > > 2 ) ) ;
w - > writeC ( 0xff ) ;
break ;
2022-12-15 07:36:51 -05:00
case DIV_SYSTEM_GA20 :
2023-04-02 01:32:47 -04:00
for ( int i = 0 ; i < 4 ; i + + ) {
2022-12-15 07:36:51 -05:00
w - > writeC ( 0xbf ) ; // mute
w - > writeC ( ( baseAddr2 | 5 ) + ( i * 8 ) ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0xbf ) ; // keyoff
w - > writeC ( ( baseAddr2 | 6 ) + ( i * 8 ) ) ;
w - > writeC ( 0 ) ;
}
break ;
2023-04-02 01:32:47 -04:00
case DIV_SYSTEM_K053260 :
for ( int i = 0 ; i < 4 ; i + + ) {
w - > writeC ( 0xba ) ; // mute
w - > writeC ( baseAddr2 | 0x2f ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0xba ) ; // keyoff
w - > writeC ( baseAddr2 | 0x28 ) ;
w - > writeC ( 0 ) ;
}
break ;
2023-08-09 07:30:00 -04:00
case DIV_SYSTEM_C140 :
for ( int i = 0 ; i < 24 ; i + + ) {
w - > writeC ( 0xd4 ) ; // mute
2023-08-13 04:31:33 -04:00
w - > writeS_BE ( baseAddr2S | ( i < < 4 ) | 0 ) ;
2023-08-09 07:30:00 -04:00
w - > writeC ( 0 ) ;
w - > writeC ( 0xd4 ) ;
2023-08-13 04:31:33 -04:00
w - > writeS_BE ( baseAddr2S | ( i < < 4 ) | 1 ) ;
2023-08-09 07:30:00 -04:00
w - > writeC ( 0 ) ;
w - > writeC ( 0xd4 ) ; // keyoff
2023-08-13 04:31:33 -04:00
w - > writeS_BE ( baseAddr2S | ( i < < 4 ) | 5 ) ;
2023-08-09 07:30:00 -04:00
w - > writeC ( 0 ) ;
}
break ;
2023-08-28 19:21:49 -04:00
case DIV_SYSTEM_C219 :
for ( int i = 0 ; i < 16 ; i + + ) {
w - > writeC ( 0xd4 ) ; // mute
w - > writeS_BE ( baseAddr2S | ( i < < 4 ) | 0 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0xd4 ) ;
w - > writeS_BE ( baseAddr2S | ( i < < 4 ) | 1 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0xd4 ) ; // keyoff
w - > writeS_BE ( baseAddr2S | ( i < < 4 ) | 5 ) ;
w - > writeC ( 0 ) ;
}
break ;
2024-07-13 00:09:10 -04:00
case DIV_SYSTEM_OPL4 :
case DIV_SYSTEM_OPL4_DRUMS :
// disable envelope
for ( int i = 0 ; i < 6 ; i + + ) {
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x00 | baseAddr2 ) ;
w - > writeC ( 0x80 + i ) ;
w - > writeC ( 0x0f ) ;
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x00 | baseAddr2 ) ;
w - > writeC ( 0x88 + i ) ;
w - > writeC ( 0x0f ) ;
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x00 | baseAddr2 ) ;
w - > writeC ( 0x90 + i ) ;
w - > writeC ( 0x0f ) ;
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x01 | baseAddr2 ) ;
w - > writeC ( 0x80 + i ) ;
w - > writeC ( 0x0f ) ;
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x01 | baseAddr2 ) ;
w - > writeC ( 0x88 + i ) ;
w - > writeC ( 0x0f ) ;
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x01 | baseAddr2 ) ;
w - > writeC ( 0x90 + i ) ;
w - > writeC ( 0x0f ) ;
}
for ( int i = 0 ; i < 24 ; i + + ) {
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x02 | baseAddr2 ) ;
w - > writeC ( 0x80 + i ) ;
w - > writeC ( 0x00 ) ;
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x02 | baseAddr2 ) ;
w - > writeC ( 0x98 + i ) ;
w - > writeC ( 0x00 ) ;
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x02 | baseAddr2 ) ;
w - > writeC ( 0xb0 + i ) ;
w - > writeC ( 0x00 ) ;
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x02 | baseAddr2 ) ;
w - > writeC ( 0xc8 + i ) ;
w - > writeC ( 0x00 ) ;
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x02 | baseAddr2 ) ;
w - > writeC ( 0xe0 + i ) ;
w - > writeC ( 0x00 ) ;
}
// key off + freq reset
for ( int i = 0 ; i < 9 ; i + + ) {
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x00 | baseAddr2 ) ;
w - > writeC ( 0xa0 + i ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x00 | baseAddr2 ) ;
w - > writeC ( 0xb0 + i ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x01 | baseAddr2 ) ;
w - > writeC ( 0xa0 + i ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x01 | baseAddr2 ) ;
w - > writeC ( 0xb0 + i ) ;
w - > writeC ( 0 ) ;
}
for ( int i = 0 ; i < 24 ; i + + ) {
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x02 | baseAddr2 ) ;
w - > writeC ( 0x20 + i ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x02 | baseAddr2 ) ;
w - > writeC ( 0x38 + i ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x02 | baseAddr2 ) ;
w - > writeC ( 0x68 + i ) ;
w - > writeC ( 8 ) ;
}
// reset 4-op
w - > writeC ( 0xd0 ) ;
w - > writeC ( 0x01 | baseAddr2 ) ;
w - > writeC ( 0x04 ) ;
w - > writeC ( 0x00 ) ;
break ;
2022-02-18 12:58:36 -05:00
default :
break ;
}
}
2023-06-22 16:20:48 -04:00
if ( write . addr = = 0xffff0004 ) { // switch sample bank
switch ( sys ) {
case DIV_SYSTEM_NES : {
2025-05-06 16:19:59 -04:00
if ( dpcm07 ) {
unsigned int bankAddr = bankOffset + ( write . val < < 14 ) ;
w - > writeC ( 0x68 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0x07 | ( isSecond ? 0x80 : 0x00 ) ) ;
w - > writeC ( bankAddr & 0xff ) ;
w - > writeC ( ( bankAddr > > 8 ) & 0xff ) ;
w - > writeC ( ( bankAddr > > 16 ) & 0xff ) ;
w - > writeC ( 0x00 ) ;
w - > writeC ( 0xc0 ) ;
w - > writeC ( 0x00 ) ;
w - > writeC ( 0x00 ) ;
w - > writeC ( 0x40 ) ;
w - > writeC ( 0x00 ) ;
} else {
// write the whole damn bank.
// this code looks like a mess because it is a hack.
// don't blame me if your VGM ends up being over a gigabyte!
size_t howMuchWillBeWritten = writeNES [ isSecond ? 1 : 0 ] - > getSampleMemUsage ( ) ;
// refuse to switch if we're going out of bounds
if ( ( write . val < < 14 ) > = howMuchWillBeWritten ) break ;
howMuchWillBeWritten - = ( write . val < < 14 ) ;
if ( howMuchWillBeWritten > 16384 ) howMuchWillBeWritten = 16384 ;
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0xc2 ) ;
2025-05-07 15:40:02 -04:00
w - > writeI ( ( isSecond ? 0x80000000 : 0 ) | ( howMuchWillBeWritten + 2 ) ) ;
2025-05-06 16:19:59 -04:00
// data
w - > writeS ( 0xc000 ) ;
w - > write ( & ( ( ( unsigned char * ) writeNES [ isSecond ? 1 : 0 ] - > getSampleMem ( ) ) [ write . val < < 14 ] ) , howMuchWillBeWritten ) ;
}
2023-06-22 16:20:48 -04:00
break ;
}
default :
break ;
}
}
2022-02-18 12:58:36 -05:00
if ( write . addr > = 0xffff0000 ) { // Furnace special command
2022-10-17 15:28:41 -04:00
if ( ! directStream ) {
unsigned char streamID = streamOff + ( ( write . addr & 0xff00 ) > > 8 ) ;
logD ( " writing stream command %x:%x with stream ID %d " , write . addr , write . val , streamID ) ;
switch ( write . addr & 0xff ) {
case 0 : // play sample
2024-08-23 00:17:22 -04:00
sampleStoppable [ streamID ] = true ;
2023-03-14 02:27:45 -04:00
if ( write . val < ( unsigned int ) song . sampleLen ) {
if ( playingSample [ streamID ] ! = ( int ) write . val ) {
2023-01-15 01:20:31 -05:00
pendingFreq [ streamID ] = write . val ;
} else {
DivSample * sample = song . sample [ write . val ] ;
2025-07-26 19:48:23 -04:00
int pos = sampleOff8 [ write . val & 0x7fff ] + setPos [ streamID ] ;
int len = ( int ) sampleLen8 [ write . val & 0x7fff ] - setPos [ streamID ] ;
2023-07-09 18:41:24 -04:00
if ( len < 0 ) len = 0 ;
if ( setPos [ streamID ] ! = 0 ) {
if ( len < = 0 ) {
w - > writeC ( 0x94 ) ;
w - > writeC ( streamID ) ;
} else {
w - > writeC ( 0x93 ) ;
w - > writeC ( streamID ) ;
w - > writeI ( pos ) ;
w - > writeC ( 1 | ( ( sample - > getLoopStartPosition ( DIV_SAMPLE_DEPTH_8BIT ) = = 0 & & sample - > isLoopable ( ) ) ? 0x80 : 0 ) | ( sampleDir [ streamID ] ? 0x10 : 0 ) ) ; // flags
w - > writeI ( len ) ;
}
} else {
w - > writeC ( 0x95 ) ;
w - > writeC ( streamID ) ;
w - > writeS ( write . val ) ; // sample number
w - > writeC ( ( sample - > getLoopStartPosition ( DIV_SAMPLE_DEPTH_8BIT ) = = 0 & & sample - > isLoopable ( ) ) | ( sampleDir [ streamID ] ? 0x10 : 0 ) ) ; // flags
}
2023-01-15 01:20:31 -05:00
if ( sample - > isLoopable ( ) & & ! sampleDir [ streamID ] ) {
2023-07-09 18:41:24 -04:00
loopTimer [ streamID ] = len ;
2023-01-15 01:20:31 -05:00
loopSample [ streamID ] = write . val ;
}
playingSample [ streamID ] = write . val ;
2023-07-09 18:41:24 -04:00
setPos [ streamID ] = 0 ;
2022-10-17 15:28:41 -04:00
}
2022-02-18 12:58:36 -05:00
}
2022-10-17 15:28:41 -04:00
break ;
2023-06-22 17:45:31 -04:00
case 1 : { // set sample freq
2024-08-23 00:17:22 -04:00
sampleStoppable [ streamID ] = true ;
2025-06-06 03:45:59 -04:00
int realFreq = ( write . val * 44100 ) / rateCorrection ;
2023-06-22 17:45:31 -04:00
if ( realFreq < 0 ) realFreq = 0 ;
if ( realFreq > 44100 ) realFreq = 44100 ;
2022-10-17 15:28:41 -04:00
w - > writeC ( 0x92 ) ;
w - > writeC ( streamID ) ;
2023-06-22 17:45:31 -04:00
w - > writeI ( realFreq ) ;
loopFreq [ streamID ] = realFreq ;
2023-01-15 01:20:31 -05:00
if ( pendingFreq [ streamID ] ! = - 1 ) {
DivSample * sample = song . sample [ pendingFreq [ streamID ] ] ;
2025-07-26 19:48:23 -04:00
int pos = sampleOff8 [ pendingFreq [ streamID ] & 0x7fff ] + setPos [ streamID ] ;
int len = ( int ) sampleLen8 [ pendingFreq [ streamID ] & 0x7fff ] - setPos [ streamID ] ;
2023-07-09 18:41:24 -04:00
if ( len < 0 ) len = 0 ;
if ( setPos [ streamID ] ! = 0 ) {
if ( len < = 0 ) {
w - > writeC ( 0x94 ) ;
w - > writeC ( streamID ) ;
} else {
w - > writeC ( 0x93 ) ;
w - > writeC ( streamID ) ;
w - > writeI ( pos ) ;
w - > writeC ( 1 | ( ( sample - > getLoopStartPosition ( DIV_SAMPLE_DEPTH_8BIT ) = = 0 & & sample - > isLoopable ( ) ) ? 0x80 : 0 ) | ( sampleDir [ streamID ] ? 0x10 : 0 ) ) ; // flags
w - > writeI ( len ) ;
}
} else {
w - > writeC ( 0x95 ) ;
w - > writeC ( streamID ) ;
w - > writeS ( pendingFreq [ streamID ] ) ; // sample number
w - > writeC ( ( sample - > getLoopStartPosition ( DIV_SAMPLE_DEPTH_8BIT ) = = 0 & & sample - > isLoopable ( ) ) | ( sampleDir [ streamID ] ? 0x10 : 0 ) ) ; // flags
}
2023-01-15 01:20:31 -05:00
if ( sample - > isLoopable ( ) & & ! sampleDir [ streamID ] ) {
2023-07-09 18:41:24 -04:00
loopTimer [ streamID ] = len ;
2023-01-15 01:46:34 -05:00
loopSample [ streamID ] = pendingFreq [ streamID ] ;
2023-01-15 01:20:31 -05:00
}
playingSample [ streamID ] = pendingFreq [ streamID ] ;
pendingFreq [ streamID ] = - 1 ;
2023-07-09 18:41:24 -04:00
setPos [ streamID ] = 0 ;
2023-01-15 01:20:31 -05:00
}
2022-10-17 15:28:41 -04:00
break ;
2023-06-22 17:45:31 -04:00
}
2022-10-17 15:28:41 -04:00
case 2 : // stop sample
2024-08-23 00:17:22 -04:00
if ( sampleStoppable [ streamID ] ) {
w - > writeC ( 0x94 ) ;
w - > writeC ( streamID ) ;
loopSample [ streamID ] = - 1 ;
playingSample [ streamID ] = - 1 ;
pendingFreq [ streamID ] = - 1 ;
sampleStoppable [ streamID ] = false ;
}
2022-10-17 15:28:41 -04:00
break ;
case 3 : // set sample direction
sampleDir [ streamID ] = write . val ;
break ;
2023-07-09 18:41:24 -04:00
case 5 : // set sample pos
setPos [ streamID ] = write . val ;
if ( playingSample [ streamID ] ! = - 1 & & pendingFreq [ streamID ] = = - 1 ) {
// play the sample again
DivSample * sample = song . sample [ playingSample [ streamID ] ] ;
2025-07-26 19:48:23 -04:00
int pos = sampleOff8 [ playingSample [ streamID ] & 0x7fff ] + setPos [ streamID ] ;
int len = ( int ) sampleLen8 [ playingSample [ streamID ] & 0x7fff ] - setPos [ streamID ] ;
2023-07-09 18:41:24 -04:00
if ( len < 0 ) len = 0 ;
if ( setPos [ streamID ] ! = 0 ) {
if ( len < = 0 ) {
w - > writeC ( 0x94 ) ;
w - > writeC ( streamID ) ;
} else {
w - > writeC ( 0x93 ) ;
w - > writeC ( streamID ) ;
w - > writeI ( pos ) ;
w - > writeC ( 1 | ( ( sample - > getLoopStartPosition ( DIV_SAMPLE_DEPTH_8BIT ) = = 0 & & sample - > isLoopable ( ) ) ? 0x80 : 0 ) | ( sampleDir [ streamID ] ? 0x10 : 0 ) ) ; // flags
w - > writeI ( len ) ;
}
} else {
w - > writeC ( 0x95 ) ;
w - > writeC ( streamID ) ;
w - > writeS ( playingSample [ streamID ] ) ; // sample number
w - > writeC ( ( sample - > getLoopStartPosition ( DIV_SAMPLE_DEPTH_8BIT ) = = 0 & & sample - > isLoopable ( ) ) | ( sampleDir [ streamID ] ? 0x10 : 0 ) ) ; // flags
}
if ( sample - > isLoopable ( ) & & ! sampleDir [ streamID ] ) {
loopTimer [ streamID ] = len ;
loopSample [ streamID ] = playingSample [ streamID ] ;
}
}
break ;
2022-10-17 15:28:41 -04:00
}
2022-02-18 12:58:36 -05:00
}
return ;
}
switch ( sys ) {
case DIV_SYSTEM_YM2612 :
2022-02-27 00:39:16 -05:00
case DIV_SYSTEM_YM2612_EXT :
2022-12-20 17:49:53 -05:00
case DIV_SYSTEM_YM2612_DUALPCM :
case DIV_SYSTEM_YM2612_DUALPCM_EXT :
2024-02-29 21:33:40 -05:00
case DIV_SYSTEM_YM2612_CSM :
2022-02-18 12:58:36 -05:00
switch ( write . addr > > 8 ) {
case 0 : // port 0
2022-03-05 04:02:01 -05:00
w - > writeC ( 2 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val ) ;
break ;
case 1 : // port 1
2022-03-05 04:02:01 -05:00
w - > writeC ( 3 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val ) ;
break ;
case 2 : // PSG
2022-03-05 04:02:01 -05:00
w - > writeC ( smsAddr ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( write . val ) ;
break ;
}
break ;
case DIV_SYSTEM_SMS :
2023-01-27 14:52:11 -05:00
if ( write . addr = = 1 ) {
w - > writeC ( ggAddr ) ;
} else {
w - > writeC ( smsAddr ) ;
}
2022-02-18 12:58:36 -05:00
w - > writeC ( write . val ) ;
break ;
2022-10-16 02:27:39 -04:00
case DIV_SYSTEM_T6W28 :
if ( write . addr ) {
w - > writeC ( 0x30 ) ;
} else {
w - > writeC ( 0x50 ) ;
}
w - > writeC ( write . val ) ;
break ;
2022-02-18 12:58:36 -05:00
case DIV_SYSTEM_GB :
w - > writeC ( 0xb3 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( baseAddr2 | ( ( write . addr - 16 ) & 0xff ) ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( write . val ) ;
break ;
case DIV_SYSTEM_PCE :
w - > writeC ( 0xb9 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( baseAddr2 | ( write . addr & 0xff ) ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( write . val ) ;
break ;
case DIV_SYSTEM_NES :
w - > writeC ( 0xb4 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( baseAddr2 | ( write . addr & 0xff ) ) ;
2022-04-03 23:37:16 -04:00
w - > writeC ( write . val ) ;
break ;
case DIV_SYSTEM_FDS : // yeah
w - > writeC ( 0xb4 ) ;
if ( ( write . addr & 0xff ) = = 0x23 ) {
w - > writeC ( baseAddr2 | 0x3f ) ;
} else if ( ( write . addr & 0xff ) > = 0x80 ) {
w - > writeC ( baseAddr2 | ( 0x20 + ( write . addr & 0x7f ) ) ) ;
} else {
w - > writeC ( baseAddr2 | ( write . addr & 0xff ) ) ;
}
2022-02-18 12:58:36 -05:00
w - > writeC ( write . val ) ;
break ;
case DIV_SYSTEM_YM2151 :
2022-03-05 04:02:01 -05:00
w - > writeC ( 4 | baseAddr1 ) ;
2022-02-27 00:39:16 -05:00
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val ) ;
break ;
case DIV_SYSTEM_SEGAPCM :
case DIV_SYSTEM_SEGAPCM_COMPAT :
w - > writeC ( 0xc0 ) ;
2022-03-05 04:02:01 -05:00
w - > writeS ( baseAddr2S | ( write . addr & 0xffff ) ) ;
2022-02-27 00:39:16 -05:00
w - > writeC ( write . val ) ;
2022-02-18 12:58:36 -05:00
break ;
2022-03-06 12:31:03 -05:00
case DIV_SYSTEM_X1_010 :
w - > writeC ( 0xc8 ) ;
2022-03-11 20:22:21 -05:00
w - > writeS_BE ( baseAddr2S | ( write . addr & 0x1fff ) ) ;
2022-03-06 12:31:03 -05:00
w - > writeC ( write . val ) ;
break ;
2022-02-18 12:58:36 -05:00
case DIV_SYSTEM_YM2610 :
case DIV_SYSTEM_YM2610_FULL :
2022-02-24 11:02:35 -05:00
case DIV_SYSTEM_YM2610B :
2022-02-18 12:58:36 -05:00
case DIV_SYSTEM_YM2610_EXT :
case DIV_SYSTEM_YM2610_FULL_EXT :
2022-02-24 11:02:35 -05:00
case DIV_SYSTEM_YM2610B_EXT :
2025-06-14 19:08:58 -04:00
case DIV_SYSTEM_YM2610_CSM :
case DIV_SYSTEM_YM2610B_CSM :
2022-02-18 12:58:36 -05:00
switch ( write . addr > > 8 ) {
case 0 : // port 0
2022-03-05 04:02:01 -05:00
w - > writeC ( 8 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val ) ;
break ;
case 1 : // port 1
2022-03-05 04:02:01 -05:00
w - > writeC ( 9 | baseAddr1 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val ) ;
break ;
}
break ;
2022-12-20 17:49:53 -05:00
case DIV_SYSTEM_YM2203 :
case DIV_SYSTEM_YM2203_EXT :
2025-06-14 19:08:58 -04:00
case DIV_SYSTEM_YM2203_CSM :
2022-05-11 04:41:02 -04:00
w - > writeC ( 5 | baseAddr1 ) ;
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val ) ;
break ;
2022-12-20 17:49:53 -05:00
case DIV_SYSTEM_YM2608 :
case DIV_SYSTEM_YM2608_EXT :
2025-06-14 19:08:58 -04:00
case DIV_SYSTEM_YM2608_CSM :
2022-05-19 19:09:46 -04:00
switch ( write . addr > > 8 ) {
case 0 : // port 0
w - > writeC ( 6 | baseAddr1 ) ;
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val ) ;
break ;
case 1 : // port 1
w - > writeC ( 7 | baseAddr1 ) ;
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val ) ;
break ;
}
break ;
2022-03-02 00:58:49 -05:00
case DIV_SYSTEM_OPLL :
case DIV_SYSTEM_OPLL_DRUMS :
case DIV_SYSTEM_VRC7 :
2022-03-05 04:02:01 -05:00
w - > writeC ( 1 | baseAddr1 ) ;
2022-03-02 00:58:49 -05:00
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val ) ;
break ;
2022-02-18 12:58:36 -05:00
case DIV_SYSTEM_AY8910 :
case DIV_SYSTEM_AY8930 :
w - > writeC ( 0xa0 ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( baseAddr2 | ( write . addr & 0xff ) ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( write . val ) ;
break ;
case DIV_SYSTEM_SAA1099 :
w - > writeC ( 0xbd ) ;
2022-03-05 04:02:01 -05:00
w - > writeC ( baseAddr2 | ( write . addr & 0xff ) ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( write . val ) ;
break ;
2022-12-20 14:54:55 -05:00
case DIV_SYSTEM_POKEY :
w - > writeC ( 0xbb ) ;
w - > writeC ( baseAddr2 | ( write . addr & 0x0f ) ) ;
w - > writeC ( write . val & 0xff ) ;
break ;
2022-02-20 12:15:15 -05:00
case DIV_SYSTEM_LYNX :
2023-01-15 10:27:20 -05:00
w - > writeC ( 0x40 ) ;
2022-02-20 12:15:15 -05:00
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val & 0xff ) ;
break ;
2022-02-22 04:01:57 -05:00
case DIV_SYSTEM_QSOUND :
w - > writeC ( 0xc4 ) ;
2022-02-22 18:21:57 -05:00
w - > writeC ( ( write . val > > 8 ) & 0xff ) ;
2022-02-22 04:01:57 -05:00
w - > writeC ( write . val & 0xff ) ;
w - > writeC ( write . addr & 0xff ) ;
break ;
2022-03-06 11:13:47 -05:00
case DIV_SYSTEM_SWAN :
if ( ( write . addr & 0x7f ) < 0x40 ) {
w - > writeC ( 0xbc ) ;
w - > writeC ( baseAddr2 | ( write . addr & 0x3f ) ) ;
w - > writeC ( write . val & 0xff ) ;
} else {
// (Wave) RAM write
w - > writeC ( 0xc6 ) ;
2022-03-06 13:26:59 -05:00
w - > writeS_BE ( baseAddr2S | ( write . addr & 0x3f ) ) ;
2022-03-06 11:13:47 -05:00
w - > writeC ( write . val & 0xff ) ;
}
break ;
2022-04-20 12:52:37 -04:00
case DIV_SYSTEM_ES5506 :
w - > writeC ( 0xbe ) ;
2025-06-01 06:19:42 -04:00
w - > writeC ( baseAddr2 | ( write . addr & 0x7f ) ) ;
2022-04-20 12:52:37 -04:00
w - > writeC ( write . val & 0xff ) ;
break ;
2022-10-09 01:00:00 -04:00
case DIV_SYSTEM_VBOY :
w - > writeC ( 0xc7 ) ;
w - > writeS_BE ( baseAddr2S | ( write . addr > > 2 ) ) ;
w - > writeC ( write . val & 0xff ) ;
break ;
2022-03-30 04:36:16 -04:00
case DIV_SYSTEM_OPL :
case DIV_SYSTEM_OPL_DRUMS :
w - > writeC ( 0x0b | baseAddr1 ) ;
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val ) ;
break ;
2022-05-19 19:09:46 -04:00
case DIV_SYSTEM_Y8950 :
case DIV_SYSTEM_Y8950_DRUMS :
w - > writeC ( 0x0c | baseAddr1 ) ;
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val ) ;
break ;
2022-03-30 04:36:16 -04:00
case DIV_SYSTEM_OPL2 :
case DIV_SYSTEM_OPL2_DRUMS :
w - > writeC ( 0x0a | baseAddr1 ) ;
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val ) ;
break ;
case DIV_SYSTEM_OPL3 :
case DIV_SYSTEM_OPL3_DRUMS :
switch ( write . addr > > 8 ) {
case 0 : // port 0
w - > writeC ( 0x0e | baseAddr1 ) ;
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val ) ;
break ;
case 1 : // port 1
w - > writeC ( 0x0f | baseAddr1 ) ;
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val ) ;
break ;
}
break ;
2022-05-11 03:20:14 -04:00
case DIV_SYSTEM_SCC :
if ( write . addr < 0x80 ) {
w - > writeC ( 0xd2 ) ;
2022-05-11 03:32:08 -04:00
w - > writeC ( baseAddr2 | 0 ) ;
w - > writeC ( write . addr & 0x7f ) ;
2022-05-11 03:20:14 -04:00
w - > writeC ( write . val & 0xff ) ;
} else if ( write . addr < 0x8a ) {
w - > writeC ( 0xd2 ) ;
2022-05-11 03:32:08 -04:00
w - > writeC ( baseAddr2 | 1 ) ;
w - > writeC ( ( write . addr - 0x80 ) & 0x7f ) ;
2022-05-11 03:20:14 -04:00
w - > writeC ( write . val & 0xff ) ;
} else if ( write . addr < 0x8f ) {
w - > writeC ( 0xd2 ) ;
2022-05-11 03:32:08 -04:00
w - > writeC ( baseAddr2 | 2 ) ;
w - > writeC ( ( write . addr - 0x8a ) & 0x7f ) ;
2022-05-11 03:20:14 -04:00
w - > writeC ( write . val & 0xff ) ;
} else if ( write . addr < 0x90 ) {
w - > writeC ( 0xd2 ) ;
2022-05-11 03:32:08 -04:00
w - > writeC ( baseAddr2 | 3 ) ;
w - > writeC ( ( write . addr - 0x8f ) & 0x7f ) ;
2022-05-11 03:20:14 -04:00
w - > writeC ( write . val & 0xff ) ;
} else if ( write . addr > = 0xe0 ) {
w - > writeC ( 0xd2 ) ;
2022-05-11 03:32:08 -04:00
w - > writeC ( baseAddr2 | 5 ) ;
w - > writeC ( ( write . addr - 0xe0 ) & 0x7f ) ;
2022-05-11 03:20:14 -04:00
w - > writeC ( write . val & 0xff ) ;
} else {
logW ( " SCC: writing to unmapped address %.2x! " , write . addr ) ;
}
break ;
2022-05-11 03:32:08 -04:00
case DIV_SYSTEM_SCC_PLUS :
if ( write . addr < 0x80 ) {
w - > writeC ( 0xd2 ) ;
w - > writeC ( baseAddr2 | 0 ) ;
w - > writeC ( write . addr & 0x7f ) ;
w - > writeC ( write . val & 0xff ) ;
} else if ( write . addr < 0xa0 ) {
w - > writeC ( 0xd2 ) ;
w - > writeC ( baseAddr2 | 4 ) ;
w - > writeC ( write . addr ) ;
w - > writeC ( write . val & 0xff ) ;
} else if ( write . addr < 0xaa ) {
w - > writeC ( 0xd2 ) ;
w - > writeC ( baseAddr2 | 1 ) ;
w - > writeC ( ( write . addr - 0xa0 ) & 0x7f ) ;
w - > writeC ( write . val & 0xff ) ;
} else if ( write . addr < 0xaf ) {
w - > writeC ( 0xd2 ) ;
w - > writeC ( baseAddr2 | 2 ) ;
w - > writeC ( ( write . addr - 0xaa ) & 0x7f ) ;
w - > writeC ( write . val & 0xff ) ;
} else if ( write . addr < 0xb0 ) {
w - > writeC ( 0xd2 ) ;
w - > writeC ( baseAddr2 | 3 ) ;
w - > writeC ( ( write . addr - 0xaf ) & 0x7f ) ;
w - > writeC ( write . val & 0xff ) ;
} else if ( write . addr > = 0xe0 ) {
w - > writeC ( 0xd2 ) ;
w - > writeC ( baseAddr2 | 5 ) ;
w - > writeC ( ( write . addr - 0xe0 ) & 0x7f ) ;
w - > writeC ( write . val & 0xff ) ;
} else {
logW ( " SCC+: writing to unmapped address %.2x! " , write . addr ) ;
}
break ;
2022-05-18 02:55:33 -04:00
case DIV_SYSTEM_YMZ280B :
w - > writeC ( 0x0d | baseAddr1 ) ;
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val & 0xff ) ;
break ;
2022-05-20 14:45:26 -04:00
case DIV_SYSTEM_RF5C68 :
w - > writeC ( rf5c68Addr ) ;
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val ) ;
break ;
2023-09-11 02:19:02 -04:00
case DIV_SYSTEM_MSM6258 :
w - > writeC ( 0xb7 ) ;
w - > writeC ( baseAddr2 | ( write . addr & 0x7f ) ) ;
w - > writeC ( write . val ) ;
logV ( " MSM write to %.2x %.2x " , write . addr , write . val ) ;
break ;
2022-05-28 19:15:26 -04:00
case DIV_SYSTEM_MSM6295 :
w - > writeC ( 0xb8 ) ;
w - > writeC ( baseAddr2 | ( write . addr & 0x7f ) ) ;
w - > writeC ( write . val ) ;
break ;
2022-12-15 07:36:51 -05:00
case DIV_SYSTEM_GA20 :
w - > writeC ( 0xbf ) ;
w - > writeC ( baseAddr2 | ( write . addr & 0x7f ) ) ;
w - > writeC ( write . val ) ;
break ;
2023-04-02 01:32:47 -04:00
case DIV_SYSTEM_K053260 :
w - > writeC ( 0xba ) ;
w - > writeC ( baseAddr2 | ( write . addr & 0x3f ) ) ;
w - > writeC ( write . val & 0xff ) ;
break ;
2023-08-09 07:30:00 -04:00
case DIV_SYSTEM_C140 :
2023-08-28 19:21:49 -04:00
case DIV_SYSTEM_C219 :
2023-08-09 07:30:00 -04:00
w - > writeC ( 0xd4 ) ;
2023-08-12 05:33:33 -04:00
w - > writeS_BE ( baseAddr2S | ( write . addr & 0x1ff ) ) ;
2023-08-09 07:30:00 -04:00
w - > writeC ( write . val & 0xff ) ;
break ;
2024-07-13 00:09:10 -04:00
case DIV_SYSTEM_OPL4 :
case DIV_SYSTEM_OPL4_DRUMS :
w - > writeC ( 0xd0 ) ;
w - > writeC ( ( ( write . addr > > 8 ) & 0x7f ) | baseAddr2 ) ;
w - > writeC ( write . addr & 0xff ) ;
w - > writeC ( write . val ) ;
break ;
2022-02-18 12:58:36 -05:00
default :
2022-04-10 23:12:02 -04:00
logW ( " write not handled! " ) ;
2022-02-18 12:58:36 -05:00
break ;
}
}
2022-10-09 18:24:24 -04:00
# define CHIP_VOL(_id,_mult) { \
2023-01-15 03:54:11 -05:00
double _vol = fabs ( ( float ) song . systemVol [ i ] ) * 256.0 * _mult ; \
2022-10-09 18:24:24 -04:00
if ( _vol < 0.0 ) _vol = 0.0 ; \
if ( _vol > 32767.0 ) _vol = 32767.0 ; \
2023-01-15 03:54:11 -05:00
chipVolSum + = ( unsigned int ) ( _vol / _mult ) ; \
chipAccounting + + ; \
2022-10-09 18:24:24 -04:00
chipVol . push_back ( ( _id ) | ( 0x80000000 ) | ( ( ( unsigned int ) _vol ) < < 16 ) ) ; \
}
# define CHIP_VOL_SECOND(_id,_mult) { \
2023-01-15 03:54:11 -05:00
double _vol = fabs ( ( float ) song . systemVol [ i ] ) * 256.0 * _mult ; \
2022-10-09 18:24:24 -04:00
if ( _vol < 0.0 ) _vol = 0.0 ; \
if ( _vol > 32767.0 ) _vol = 32767.0 ; \
2023-01-15 03:54:11 -05:00
chipVolSum + = ( unsigned int ) ( _vol / _mult ) ; \
chipAccounting + + ; \
2022-10-09 18:24:24 -04:00
chipVol . push_back ( ( _id ) | ( 0x80000100 ) | ( ( ( unsigned int ) _vol ) < < 16 ) ) ; \
}
2025-06-06 03:45:59 -04:00
SafeWriter * DivEngine : : saveVGM ( bool * sysToExport , bool loop , int version , bool patternHints , bool directStream , int trailingTicks , bool dpcm07 , int correctedRate ) {
2022-04-01 22:40:32 -04:00
if ( version < 0x150 ) {
lastError = " VGM version is too low " ;
return NULL ;
}
2022-02-18 12:58:36 -05:00
stop ( ) ;
2022-02-21 23:05:41 -05:00
repeatPattern = false ;
2022-02-18 12:58:36 -05:00
setOrder ( 0 ) ;
2022-03-23 22:38:28 -04:00
BUSY_BEGIN_SOFT ;
2022-02-18 12:58:36 -05:00
double origRate = got . rate ;
2025-06-06 03:45:59 -04:00
got . rate = correctedRate ;
2022-02-18 12:58:36 -05:00
// determine loop point
int loopOrder = 0 ;
int loopRow = 0 ;
int loopEnd = 0 ;
walkSong ( loopOrder , loopRow , loopEnd ) ;
2022-04-10 23:12:02 -04:00
logI ( " loop point: %d %d " , loopOrder , loopRow ) ;
2022-02-18 12:58:36 -05:00
warnings = " " ;
curOrder = 0 ;
freelance = false ;
playing = false ;
extValuePresent = false ;
remainingLoops = - 1 ;
// play the song ourselves
bool done = false ;
int writeCount = 0 ;
int gd3Off = 0 ;
int hasSN = 0 ;
int snNoiseConfig = 9 ;
int snNoiseSize = 16 ;
int snFlags = 0 ;
int hasOPLL = 0 ;
int hasOPN2 = 0 ;
int hasOPM = 0 ;
int hasSegaPCM = 0 ;
int segaPCMOffset = 0xf8000d ;
int hasRFC = 0 ;
int hasOPN = 0 ;
int hasOPNA = 0 ;
int hasOPNB = 0 ;
int hasOPL2 = 0 ;
int hasOPL = 0 ;
int hasY8950 = 0 ;
int hasOPL3 = 0 ;
int hasOPL4 = 0 ;
int hasOPX = 0 ;
int hasZ280 = 0 ;
int hasRFC1 = 0 ;
int hasPWM = 0 ;
int hasAY = 0 ;
int ayConfig = 0 ;
int ayFlags = 0 ;
int hasGB = 0 ;
int hasNES = 0 ;
int hasMultiPCM = 0 ;
int hasuPD7759 = 0 ;
int hasOKIM6258 = 0 ;
int hasK054539 = 0 ;
int hasOKIM6295 = 0 ;
int hasK051649 = 0 ;
int hasPCE = 0 ;
2023-08-28 19:21:49 -04:00
int hasC140 = 0 ;
int c140Type = 0 ;
2022-02-18 12:58:36 -05:00
int hasK053260 = 0 ;
int hasPOKEY = 0 ;
int hasQSound = 0 ;
int hasSCSP = 0 ;
int hasSwan = 0 ;
int hasVSU = 0 ;
int hasSAA = 0 ;
int hasES5503 = 0 ;
int hasES5505 = 0 ;
int hasX1 = 0 ;
int hasC352 = 0 ;
int hasGA20 = 0 ;
2022-02-20 12:15:15 -05:00
int hasLynx = 0 ;
2022-02-18 12:58:36 -05:00
int howManyChips = 0 ;
2023-01-15 03:54:11 -05:00
int chipVolSum = 0 ;
int chipAccounting = 0 ;
2022-02-18 12:58:36 -05:00
int loopPos = - 1 ;
2023-02-08 19:25:03 -05:00
int loopTickSong = - 1 ;
int songTick = 0 ;
2022-02-18 12:58:36 -05:00
2025-07-26 19:48:23 -04:00
unsigned int * sampleOff8 = new unsigned int [ 32768 ] ;
unsigned int * sampleLen8 = new unsigned int [ 32768 ] ;
unsigned int * sampleOffSegaPCM = new unsigned int [ 32768 ] ;
2022-09-25 21:07:21 -04:00
2022-02-18 12:58:36 -05:00
SafeWriter * w = new SafeWriter ;
w - > init ( ) ;
// write header
w - > write ( " Vgm " , 4 ) ;
w - > writeI ( 0 ) ; // will be written later
2022-04-01 22:40:32 -04:00
w - > writeI ( version ) ;
2022-02-18 12:58:36 -05:00
2022-12-09 00:30:27 -05:00
bool willExport [ DIV_MAX_CHIPS ] ;
bool isSecond [ DIV_MAX_CHIPS ] ;
int streamIDs [ DIV_MAX_CHIPS ] ;
2023-06-22 16:20:48 -04:00
size_t bankOffset [ DIV_MAX_CHIPS ] ;
2022-02-18 12:58:36 -05:00
double loopTimer [ DIV_MAX_CHANS ] ;
double loopFreq [ DIV_MAX_CHANS ] ;
int loopSample [ DIV_MAX_CHANS ] ;
2022-05-27 03:47:44 -04:00
bool sampleDir [ DIV_MAX_CHANS ] ;
2023-01-15 01:20:31 -05:00
int pendingFreq [ DIV_MAX_CHANS ] ;
int playingSample [ DIV_MAX_CHANS ] ;
2024-08-23 00:17:22 -04:00
bool sampleStoppable [ DIV_MAX_CHANS ] ;
2023-07-09 18:41:24 -04:00
int setPos [ DIV_MAX_CHANS ] ;
2023-01-15 01:20:31 -05:00
std : : vector < unsigned int > chipVol ;
2022-12-09 00:30:27 -05:00
std : : vector < DivDelayedWrite > delayedWrites [ DIV_MAX_CHIPS ] ;
2022-10-17 15:25:30 -04:00
std : : vector < std : : pair < int , DivDelayedWrite > > sortedWrites ;
2023-02-08 18:04:09 -05:00
std : : vector < size_t > tickPos ;
2023-02-08 19:25:03 -05:00
std : : vector < int > tickSample ;
bool trailing = false ;
bool beenOneLoopAlready = false ;
2023-04-20 05:16:56 -04:00
bool mayWriteRate = ( fmod ( curSubSong - > hz , 1.0 ) < 0.00001 | | fmod ( curSubSong - > hz , 1.0 ) > 0.99999 ) ;
2023-02-08 19:25:03 -05:00
int countDown = MAX ( 0 , trailingTicks ) + 1 ;
2022-02-18 12:58:36 -05:00
2023-06-22 16:20:48 -04:00
memset ( bankOffset , 0 , DIV_MAX_CHIPS * sizeof ( size_t ) ) ;
2022-02-18 12:58:36 -05:00
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
loopTimer [ i ] = 0 ;
loopFreq [ i ] = 0 ;
loopSample [ i ] = - 1 ;
2023-01-15 01:20:31 -05:00
pendingFreq [ i ] = - 1 ;
playingSample [ i ] = - 1 ;
2023-07-09 18:41:24 -04:00
setPos [ i ] = 0 ;
2022-05-27 03:47:44 -04:00
sampleDir [ i ] = false ;
2024-08-23 00:17:22 -04:00
sampleStoppable [ i ] = true ;
2022-02-18 12:58:36 -05:00
}
bool writeDACSamples = false ;
bool writeNESSamples = false ;
bool writePCESamples = false ;
2023-09-11 02:19:02 -04:00
bool writeVOXSamples = false ;
2024-10-17 16:06:13 -04:00
bool writeLynxSamples = false ;
2022-05-19 19:09:46 -04:00
DivDispatch * writeADPCM_OPNA [ 2 ] = { NULL , NULL } ;
DivDispatch * writeADPCM_OPNB [ 2 ] = { NULL , NULL } ;
DivDispatch * writeADPCM_Y8950 [ 2 ] = { NULL , NULL } ;
2023-02-10 02:01:23 -05:00
DivDispatch * writeSegaPCM [ 2 ] = { NULL , NULL } ;
2022-05-01 13:57:44 -04:00
DivDispatch * writeX1010 [ 2 ] = { NULL , NULL } ;
DivDispatch * writeQSound [ 2 ] = { NULL , NULL } ;
2022-05-02 22:07:50 -04:00
DivDispatch * writeES5506 [ 2 ] = { NULL , NULL } ;
2022-05-18 02:55:33 -04:00
DivDispatch * writeZ280 [ 2 ] = { NULL , NULL } ;
2022-05-20 14:45:26 -04:00
DivDispatch * writeRF5C68 [ 2 ] = { NULL , NULL } ;
2022-05-28 19:15:26 -04:00
DivDispatch * writeMSM6295 [ 2 ] = { NULL , NULL } ;
2022-12-15 07:36:51 -05:00
DivDispatch * writeGA20 [ 2 ] = { NULL , NULL } ;
2023-04-02 01:32:47 -04:00
DivDispatch * writeK053260 [ 2 ] = { NULL , NULL } ;
2023-08-09 07:30:00 -04:00
DivDispatch * writeC140 [ 2 ] = { NULL , NULL } ;
2023-08-28 19:21:49 -04:00
DivDispatch * writeC219 [ 2 ] = { NULL , NULL } ;
2023-06-22 04:24:39 -04:00
DivDispatch * writeNES [ 2 ] = { NULL , NULL } ;
2024-07-13 00:15:40 -04:00
DivDispatch * writePCM_OPL4 [ 2 ] = { NULL , NULL } ;
2023-06-22 16:20:48 -04:00
int writeNESIndex [ 2 ] = { 0 , 0 } ;
2022-02-18 12:58:36 -05:00
2023-06-22 05:55:12 -04:00
size_t bankOffsetNESCurrent = 0 ;
size_t bankOffsetNES [ 2 ] = { 0 , 0 } ;
2022-02-18 12:58:36 -05:00
for ( int i = 0 ; i < song . systemLen ; i + + ) {
willExport [ i ] = false ;
isSecond [ i ] = false ;
streamIDs [ i ] = 0 ;
if ( sysToExport ! = NULL ) {
if ( ! sysToExport [ i ] ) continue ;
}
2022-04-01 22:40:32 -04:00
if ( minVGMVersion ( song . system [ i ] ) > version ) continue ;
2022-02-18 12:58:36 -05:00
switch ( song . system [ i ] ) {
case DIV_SYSTEM_SMS :
if ( ! hasSN ) {
hasSN = disCont [ i ] . dispatch - > chipClock ;
2023-01-17 01:11:08 -05:00
CHIP_VOL ( 0 , 4.0 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
2022-09-30 01:26:54 -04:00
switch ( song . systemFlags [ i ] . getInt ( " chipType " , 0 ) ) {
2022-02-18 12:58:36 -05:00
case 1 : // real SN
snNoiseConfig = 3 ;
snNoiseSize = 15 ;
break ;
case 2 : // real SN atari bass (seemingly unsupported)
snNoiseConfig = 3 ;
snNoiseSize = 15 ;
break ;
default : // Sega VDP
snNoiseConfig = 9 ;
snNoiseSize = 16 ;
break ;
}
} else if ( ! ( hasSN & 0x40000000 ) ) {
isSecond [ i ] = true ;
willExport [ i ] = true ;
2023-01-17 01:11:08 -05:00
CHIP_VOL_SECOND ( 0 , 4.0 ) ;
2022-02-18 12:58:36 -05:00
hasSN | = 0x40000000 ;
howManyChips + + ;
}
break ;
case DIV_SYSTEM_GB :
if ( ! hasGB ) {
hasGB = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 19 , 0.75 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
} else if ( ! ( hasGB & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 19 , 0.75 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
hasGB | = 0x40000000 ;
howManyChips + + ;
}
break ;
case DIV_SYSTEM_PCE :
if ( ! hasPCE ) {
2025-03-04 12:19:46 -05:00
// the clock is halved in VGM...
hasPCE = disCont [ i ] . dispatch - > chipClock / 2 ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 27 , 0.98 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
writePCESamples = true ;
} else if ( ! ( hasPCE & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 27 , 0.98 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
hasPCE | = 0x40000000 ;
howManyChips + + ;
}
break ;
case DIV_SYSTEM_NES :
if ( ! hasNES ) {
hasNES = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 20 , 1.7 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
writeNESSamples = true ;
2023-06-22 04:24:39 -04:00
writeNES [ 0 ] = disCont [ i ] . dispatch ;
2023-06-22 16:20:48 -04:00
writeNESIndex [ 0 ] = i ;
2022-02-18 12:58:36 -05:00
} else if ( ! ( hasNES & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 20 , 1.7 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
hasNES | = 0x40000000 ;
2023-06-22 04:24:39 -04:00
writeNES [ 1 ] = disCont [ i ] . dispatch ;
2023-06-22 16:20:48 -04:00
writeNESIndex [ 1 ] = i ;
2022-02-18 12:58:36 -05:00
howManyChips + + ;
}
break ;
2022-02-27 00:39:16 -05:00
case DIV_SYSTEM_SEGAPCM :
case DIV_SYSTEM_SEGAPCM_COMPAT :
2022-02-18 12:58:36 -05:00
if ( ! hasSegaPCM ) {
hasSegaPCM = 4000000 ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 4 , 0.67 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
2023-02-10 02:01:23 -05:00
writeSegaPCM [ 0 ] = disCont [ i ] . dispatch ;
2022-02-18 12:58:36 -05:00
} else if ( ! ( hasSegaPCM & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 4 , 0.67 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
2023-02-10 02:01:23 -05:00
writeSegaPCM [ 1 ] = disCont [ i ] . dispatch ;
2022-02-18 12:58:36 -05:00
hasSegaPCM | = 0x40000000 ;
howManyChips + + ;
}
break ;
2022-03-06 12:31:03 -05:00
case DIV_SYSTEM_X1_010 :
2022-03-12 09:39:38 -05:00
if ( ! hasX1 ) {
hasX1 = disCont [ i ] . dispatch - > chipClock ;
2023-01-17 01:11:08 -05:00
CHIP_VOL ( 38 , 2.0 ) ;
2022-03-06 12:31:03 -05:00
willExport [ i ] = true ;
2022-05-01 13:57:44 -04:00
writeX1010 [ 0 ] = disCont [ i ] . dispatch ;
2022-03-12 09:39:38 -05:00
} else if ( ! ( hasX1 & 0x40000000 ) ) {
2022-03-06 12:31:03 -05:00
isSecond [ i ] = true ;
2023-01-17 01:11:08 -05:00
CHIP_VOL_SECOND ( 38 , 2.0 ) ;
2022-03-06 12:31:03 -05:00
willExport [ i ] = true ;
2022-05-01 13:57:44 -04:00
writeX1010 [ 1 ] = disCont [ i ] . dispatch ;
2022-03-12 09:39:38 -05:00
hasX1 | = 0x40000000 ;
2022-03-06 12:31:03 -05:00
howManyChips + + ;
}
break ;
2022-02-18 12:58:36 -05:00
case DIV_SYSTEM_YM2610 :
case DIV_SYSTEM_YM2610_FULL :
2022-02-24 11:02:35 -05:00
case DIV_SYSTEM_YM2610B :
2022-02-18 12:58:36 -05:00
case DIV_SYSTEM_YM2610_EXT :
case DIV_SYSTEM_YM2610_FULL_EXT :
2022-02-24 11:02:35 -05:00
case DIV_SYSTEM_YM2610B_EXT :
2025-06-14 19:08:58 -04:00
case DIV_SYSTEM_YM2610_CSM :
case DIV_SYSTEM_YM2610B_CSM :
2022-02-18 12:58:36 -05:00
if ( ! hasOPNB ) {
hasOPNB = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 8 , 1.0 ) ;
CHIP_VOL ( 0x88 , 1.25 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
2022-05-19 19:09:46 -04:00
writeADPCM_OPNB [ 0 ] = disCont [ i ] . dispatch ;
2022-02-18 12:58:36 -05:00
} else if ( ! ( hasOPNB & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 8 , 1.0 ) ;
CHIP_VOL_SECOND ( 0x88 , 1.25 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
2022-05-19 19:09:46 -04:00
writeADPCM_OPNB [ 1 ] = disCont [ i ] . dispatch ;
2022-02-18 12:58:36 -05:00
hasOPNB | = 0x40000000 ;
howManyChips + + ;
}
2025-06-14 19:08:58 -04:00
if ( ( ( song . system [ i ] = = DIV_SYSTEM_YM2610B ) | | ( song . system [ i ] = = DIV_SYSTEM_YM2610B_EXT ) | | ( song . system [ i ] = = DIV_SYSTEM_YM2610B_CSM ) ) & & ( ! ( hasOPNB & 0x80000000 ) ) ) { // YM2610B flag
2022-02-24 11:02:35 -05:00
hasOPNB | = 0x80000000 ;
2022-03-07 10:15:21 -05:00
}
2022-02-18 12:58:36 -05:00
break ;
case DIV_SYSTEM_AY8910 :
2022-05-14 11:58:00 -04:00
case DIV_SYSTEM_AY8930 : {
2022-02-18 12:58:36 -05:00
if ( ! hasAY ) {
2022-05-14 11:58:00 -04:00
bool hasClockDivider = false ; // Configurable clock divider
bool hasStereo = true ; // Stereo
2022-02-18 12:58:36 -05:00
hasAY = disCont [ i ] . dispatch - > chipClock ;
ayFlags = 1 ;
2022-05-14 11:58:00 -04:00
if ( song . system [ i ] = = DIV_SYSTEM_AY8930 ) { // AY8930
ayConfig = 0x03 ;
hasClockDivider = true ;
} else {
2022-09-30 01:26:54 -04:00
switch ( song . systemFlags [ i ] . getInt ( " chipType " , 0 ) ) {
2022-05-14 11:58:00 -04:00
case 1 : // YM2149
ayConfig = 0x10 ;
hasClockDivider = true ;
break ;
case 2 : // Sunsoft 5B
ayConfig = 0x10 ;
ayFlags | = 0x12 ; // Clock internally divided, Single sound output
hasStereo = false ; // due to above, can't be per-channel stereo configurable
break ;
case 3 : // AY8914
ayConfig = 0x04 ;
break ;
2022-09-30 01:26:54 -04:00
default : // AY8910
ayConfig = 0x00 ;
break ;
2022-05-14 11:58:00 -04:00
}
}
2022-09-30 01:26:54 -04:00
if ( hasClockDivider & & song . systemFlags [ i ] . getBool ( " halfClock " , false ) ) {
2022-05-14 11:58:00 -04:00
ayFlags | = 0x10 ;
}
2022-09-30 01:26:54 -04:00
if ( hasStereo & & song . systemFlags [ i ] . getBool ( " stereo " , false ) ) {
2022-05-14 11:58:00 -04:00
ayFlags | = 0x80 ;
}
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 18 , 1.0 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
} else if ( ! ( hasAY & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 18 , 1.0 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
hasAY | = 0x40000000 ;
howManyChips + + ;
}
break ;
2022-05-14 11:58:00 -04:00
}
2022-02-18 12:58:36 -05:00
case DIV_SYSTEM_SAA1099 :
if ( ! hasSAA ) {
hasSAA = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 35 , 1.0 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
} else if ( ! ( hasSAA & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 35 , 1.0 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
hasSAA | = 0x40000000 ;
howManyChips + + ;
}
break ;
case DIV_SYSTEM_YM2612 :
2022-02-27 00:41:27 -05:00
case DIV_SYSTEM_YM2612_EXT :
2022-12-20 17:49:53 -05:00
case DIV_SYSTEM_YM2612_DUALPCM :
case DIV_SYSTEM_YM2612_DUALPCM_EXT :
2024-02-29 21:33:40 -05:00
case DIV_SYSTEM_YM2612_CSM :
2022-02-18 12:58:36 -05:00
if ( ! hasOPN2 ) {
hasOPN2 = disCont [ i ] . dispatch - > chipClock ;
2023-01-17 01:11:08 -05:00
CHIP_VOL ( 2 , 1.6 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
writeDACSamples = true ;
} else if ( ! ( hasOPN2 & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-17 01:11:08 -05:00
CHIP_VOL_SECOND ( 2 , 1.6 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
hasOPN2 | = 0x40000000 ;
howManyChips + + ;
}
break ;
case DIV_SYSTEM_YM2151 :
if ( ! hasOPM ) {
hasOPM = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 3 , 1.0 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
} else if ( ! ( hasOPM & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 3 , 1.0 ) ;
2022-02-18 12:58:36 -05:00
willExport [ i ] = true ;
hasOPM | = 0x40000000 ;
howManyChips + + ;
}
2022-02-21 13:58:14 -05:00
break ;
2022-12-20 17:49:53 -05:00
case DIV_SYSTEM_YM2203 :
case DIV_SYSTEM_YM2203_EXT :
2025-06-14 19:08:58 -04:00
case DIV_SYSTEM_YM2203_CSM :
2022-05-11 04:41:02 -04:00
if ( ! hasOPN ) {
hasOPN = disCont [ i ] . dispatch - > chipClock ;
willExport [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 6 , 1.0 ) ;
CHIP_VOL ( 0x86 , 1.7 ) ;
2022-05-11 04:41:02 -04:00
} else if ( ! ( hasOPN & 0x40000000 ) ) {
isSecond [ i ] = true ;
willExport [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 6 , 1.0 ) ;
CHIP_VOL_SECOND ( 0x86 , 1.7 ) ;
2022-05-11 04:41:02 -04:00
hasOPN | = 0x40000000 ;
howManyChips + + ;
}
break ;
2022-12-20 17:49:53 -05:00
case DIV_SYSTEM_YM2608 :
case DIV_SYSTEM_YM2608_EXT :
2025-06-14 19:08:58 -04:00
case DIV_SYSTEM_YM2608_CSM :
2022-05-19 19:09:46 -04:00
if ( ! hasOPNA ) {
hasOPNA = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 7 , 1.0 ) ;
CHIP_VOL ( 0x87 , 1.3 ) ;
2022-05-19 19:09:46 -04:00
willExport [ i ] = true ;
writeADPCM_OPNA [ 0 ] = disCont [ i ] . dispatch ;
} else if ( ! ( hasOPNA & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 7 , 1.0 ) ;
CHIP_VOL_SECOND ( 0x87 , 1.3 ) ;
2022-05-19 19:09:46 -04:00
willExport [ i ] = true ;
writeADPCM_OPNA [ 1 ] = disCont [ i ] . dispatch ;
hasOPNA | = 0x40000000 ;
howManyChips + + ;
}
break ;
2022-03-02 00:58:49 -05:00
case DIV_SYSTEM_OPLL :
case DIV_SYSTEM_OPLL_DRUMS :
case DIV_SYSTEM_VRC7 :
if ( ! hasOPLL ) {
hasOPLL = disCont [ i ] . dispatch - > chipClock ;
2023-01-17 01:11:08 -05:00
CHIP_VOL ( 1 , 3.2 ) ;
2022-03-02 00:58:49 -05:00
willExport [ i ] = true ;
} else if ( ! ( hasOPLL & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-17 01:11:08 -05:00
CHIP_VOL_SECOND ( 1 , 3.2 ) ;
2022-03-02 00:58:49 -05:00
willExport [ i ] = true ;
hasOPLL | = 0x40000000 ;
howManyChips + + ;
}
break ;
2022-04-03 23:37:16 -04:00
case DIV_SYSTEM_FDS :
if ( ! hasNES ) {
hasNES = 0x80000000 | disCont [ i ] . dispatch - > chipClock ;
willExport [ i ] = true ;
} else if ( ! ( hasNES & 0x80000000 ) ) {
hasNES | = 0x80000000 ;
willExport [ i ] = true ;
} else if ( ! ( hasNES & 0x40000000 ) ) {
isSecond [ i ] = true ;
willExport [ i ] = true ;
hasNES | = 0xc0000000 ;
howManyChips + + ;
}
break ;
2022-12-20 14:54:55 -05:00
case DIV_SYSTEM_POKEY :
if ( ! hasPOKEY ) {
hasPOKEY = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 30 , 0.8 ) ;
2022-12-20 14:54:55 -05:00
willExport [ i ] = true ;
} else if ( ! ( hasPOKEY & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 30 , 0.8 ) ;
2022-12-20 14:54:55 -05:00
willExport [ i ] = true ;
hasPOKEY | = 0x40000000 ;
howManyChips + + ;
}
break ;
2022-02-20 12:15:15 -05:00
case DIV_SYSTEM_LYNX :
if ( ! hasLynx ) {
hasLynx = disCont [ i ] . dispatch - > chipClock ;
2022-02-22 18:21:57 -05:00
willExport [ i ] = true ;
2024-10-17 16:06:13 -04:00
writeLynxSamples = true ;
2022-02-20 12:15:15 -05:00
} else if ( ! ( hasLynx & 0x40000000 ) ) {
isSecond [ i ] = true ;
willExport [ i ] = true ;
hasLynx | = 0x40000000 ;
howManyChips + + ;
}
2022-02-18 12:58:36 -05:00
break ;
2022-02-22 04:01:57 -05:00
case DIV_SYSTEM_QSOUND :
if ( ! hasQSound ) {
2022-02-22 18:21:57 -05:00
// could set chipClock to 4000000 here for compatibility
// However I think it it not necessary because old VGM players will still
// not be able to handle the 64kb sample bank trick
2022-02-22 04:01:57 -05:00
hasQSound = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 31 , 1.0 ) ;
2022-02-22 04:01:57 -05:00
willExport [ i ] = true ;
2022-05-01 13:57:44 -04:00
writeQSound [ 0 ] = disCont [ i ] . dispatch ;
2022-02-22 04:01:57 -05:00
} else if ( ! ( hasQSound & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 31 , 1.0 ) ;
2022-02-22 04:01:57 -05:00
willExport [ i ] = false ;
2022-05-01 13:57:44 -04:00
writeQSound [ 1 ] = disCont [ i ] . dispatch ;
2022-02-22 04:01:57 -05:00
addWarning ( " dual QSound is not supported by the VGM format " ) ;
}
break ;
2022-03-06 11:13:47 -05:00
case DIV_SYSTEM_SWAN :
if ( ! hasSwan ) {
hasSwan = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 33 , 1.0 ) ;
2022-03-06 11:13:47 -05:00
willExport [ i ] = true ;
// funny enough, VGM doesn't have support for WSC's sound DMA by design
// so DAC stream it goes
// since WS has the same PCM format as YM2612 DAC, I can just reuse this flag
writeDACSamples = true ;
} else if ( ! ( hasSwan & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 33 , 1.0 ) ;
2022-03-06 11:13:47 -05:00
willExport [ i ] = true ;
hasSwan | = 0x40000000 ;
howManyChips + + ;
}
break ;
2022-04-20 12:52:37 -04:00
case DIV_SYSTEM_ES5506 :
if ( ! hasES5505 ) {
// VGM identifies ES5506 if highest bit sets, otherwise ES5505
hasES5505 = 0x80000000 | disCont [ i ] . dispatch - > chipClock ;
willExport [ i ] = true ;
2022-05-02 22:07:50 -04:00
writeES5506 [ 0 ] = disCont [ i ] . dispatch ;
2022-04-20 12:52:37 -04:00
} else if ( ! ( hasES5505 & 0x40000000 ) ) {
isSecond [ i ] = true ;
willExport [ i ] = false ;
hasES5505 | = 0xc0000000 ;
2022-05-02 22:07:50 -04:00
writeES5506 [ 1 ] = disCont [ i ] . dispatch ;
2022-04-20 12:52:37 -04:00
howManyChips + + ;
}
break ;
2022-10-09 01:00:00 -04:00
case DIV_SYSTEM_VBOY :
if ( ! hasVSU ) {
hasVSU = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 34 , 0.72 ) ;
2022-10-09 01:00:00 -04:00
willExport [ i ] = true ;
} else if ( ! ( hasVSU & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 34 , 0.72 ) ;
2022-10-09 01:00:00 -04:00
willExport [ i ] = true ;
hasVSU | = 0x40000000 ;
howManyChips + + ;
}
break ;
2022-03-30 04:36:16 -04:00
case DIV_SYSTEM_OPL :
case DIV_SYSTEM_OPL_DRUMS :
if ( ! hasOPL ) {
hasOPL = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 9 , 1.0 ) ;
2022-03-30 04:36:16 -04:00
willExport [ i ] = true ;
} else if ( ! ( hasOPL & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 9 , 1.0 ) ;
2022-03-30 04:36:16 -04:00
willExport [ i ] = true ;
hasOPL | = 0x40000000 ;
howManyChips + + ;
}
break ;
2022-05-19 19:09:46 -04:00
case DIV_SYSTEM_Y8950 :
case DIV_SYSTEM_Y8950_DRUMS :
if ( ! hasY8950 ) {
hasY8950 = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 11 , 1.0 ) ;
2022-05-19 19:09:46 -04:00
willExport [ i ] = true ;
writeADPCM_Y8950 [ 0 ] = disCont [ i ] . dispatch ;
} else if ( ! ( hasY8950 & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 11 , 1.0 ) ;
2022-05-19 19:09:46 -04:00
willExport [ i ] = true ;
writeADPCM_Y8950 [ 1 ] = disCont [ i ] . dispatch ;
hasY8950 | = 0x40000000 ;
howManyChips + + ;
}
break ;
2022-03-30 04:36:16 -04:00
case DIV_SYSTEM_OPL2 :
case DIV_SYSTEM_OPL2_DRUMS :
if ( ! hasOPL2 ) {
hasOPL2 = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 10 , 1.0 ) ;
2022-03-30 04:36:16 -04:00
willExport [ i ] = true ;
} else if ( ! ( hasOPL2 & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 10 , 1.0 ) ;
2022-03-30 04:36:16 -04:00
willExport [ i ] = true ;
hasOPL2 | = 0x40000000 ;
howManyChips + + ;
}
break ;
case DIV_SYSTEM_OPL3 :
case DIV_SYSTEM_OPL3_DRUMS :
if ( ! hasOPL3 ) {
hasOPL3 = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 12 , 1.0 ) ;
2022-03-30 04:36:16 -04:00
willExport [ i ] = true ;
} else if ( ! ( hasOPL3 & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 12 , 1.0 ) ;
2022-03-30 04:36:16 -04:00
willExport [ i ] = true ;
hasOPL3 | = 0x40000000 ;
howManyChips + + ;
}
break ;
2022-05-11 03:20:14 -04:00
case DIV_SYSTEM_SCC :
case DIV_SYSTEM_SCC_PLUS :
if ( ! hasK051649 ) {
hasK051649 = disCont [ i ] . dispatch - > chipClock ;
if ( song . system [ i ] = = DIV_SYSTEM_SCC_PLUS ) {
hasK051649 | = 0x80000000 ;
}
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 25 , 1.0 ) ;
2022-05-11 03:20:14 -04:00
willExport [ i ] = true ;
} else if ( ! ( hasK051649 & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 25 , 1.0 ) ;
2022-05-11 03:20:14 -04:00
willExport [ i ] = true ;
hasK051649 | = 0x40000000 ;
if ( song . system [ i ] = = DIV_SYSTEM_SCC_PLUS ) {
hasK051649 | = 0x80000000 ;
}
howManyChips + + ;
}
break ;
2022-05-18 02:55:33 -04:00
case DIV_SYSTEM_YMZ280B :
if ( ! hasZ280 ) {
hasZ280 = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 15 , 0.72 ) ;
2022-05-18 02:55:33 -04:00
willExport [ i ] = true ;
writeZ280 [ 0 ] = disCont [ i ] . dispatch ;
} else if ( ! ( hasZ280 & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 15 , 0.72 ) ;
2022-05-18 02:55:33 -04:00
willExport [ i ] = true ;
writeZ280 [ 1 ] = disCont [ i ] . dispatch ;
hasZ280 | = 0x40000000 ;
howManyChips + + ;
}
break ;
2022-05-20 14:45:26 -04:00
case DIV_SYSTEM_RF5C68 :
// here's the dumb part: VGM thinks RF5C68 and RF5C164 are different
// chips even though the only difference is the output resolution
// these system types are currently handled by reusing isSecond flag
// also this system is not dual-able
2022-09-30 01:26:54 -04:00
if ( song . systemFlags [ i ] . getInt ( " chipType " , 0 ) = = 1 ) {
2022-05-20 14:45:26 -04:00
if ( ! hasRFC1 ) {
hasRFC1 = disCont [ i ] . dispatch - > chipClock ;
isSecond [ i ] = true ;
2023-09-24 20:13:08 -04:00
CHIP_VOL ( 16 , 0.8 ) ;
2022-05-20 14:45:26 -04:00
willExport [ i ] = true ;
writeRF5C68 [ 1 ] = disCont [ i ] . dispatch ;
}
} else if ( ! hasRFC ) {
hasRFC = disCont [ i ] . dispatch - > chipClock ;
2023-09-24 20:13:08 -04:00
CHIP_VOL ( 5 , 1.1 ) ;
2022-05-20 14:45:26 -04:00
willExport [ i ] = true ;
writeRF5C68 [ 0 ] = disCont [ i ] . dispatch ;
2022-05-20 16:36:11 -04:00
}
2022-05-20 14:45:26 -04:00
break ;
2023-09-11 02:19:02 -04:00
case DIV_SYSTEM_MSM6258 :
if ( ! hasOKIM6258 ) {
hasOKIM6258 = disCont [ i ] . dispatch - > chipClock ;
CHIP_VOL ( 23 , 0.65 ) ;
willExport [ i ] = true ;
writeVOXSamples = true ;
} else if ( ! ( hasOKIM6258 & 0x40000000 ) ) {
isSecond [ i ] = true ;
CHIP_VOL_SECOND ( 23 , 0.65 ) ;
willExport [ i ] = true ;
writeVOXSamples = true ;
hasOKIM6258 | = 0x40000000 ;
howManyChips + + ;
}
break ;
2022-05-28 19:15:26 -04:00
case DIV_SYSTEM_MSM6295 :
if ( ! hasOKIM6295 ) {
hasOKIM6295 = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 24 , 1.0 ) ;
2022-05-28 19:15:26 -04:00
willExport [ i ] = true ;
writeMSM6295 [ 0 ] = disCont [ i ] . dispatch ;
} else if ( ! ( hasOKIM6295 & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 24 , 1.0 ) ;
2022-05-28 19:15:26 -04:00
willExport [ i ] = true ;
writeMSM6295 [ 1 ] = disCont [ i ] . dispatch ;
hasOKIM6295 | = 0x40000000 ;
howManyChips + + ;
}
break ;
2022-12-15 07:36:51 -05:00
case DIV_SYSTEM_GA20 :
if ( ! hasGA20 ) {
hasGA20 = disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 40 , 0.4 ) ;
2022-12-15 07:36:51 -05:00
willExport [ i ] = true ;
writeGA20 [ 0 ] = disCont [ i ] . dispatch ;
} else if ( ! ( hasGA20 & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-01-15 03:54:11 -05:00
CHIP_VOL_SECOND ( 40 , 0.4 ) ;
2022-12-15 07:36:51 -05:00
willExport [ i ] = true ;
writeGA20 [ 1 ] = disCont [ i ] . dispatch ;
hasGA20 | = 0x40000000 ;
howManyChips + + ;
}
break ;
2023-04-02 01:32:47 -04:00
case DIV_SYSTEM_K053260 :
if ( ! hasK053260 ) {
hasK053260 = disCont [ i ] . dispatch - > chipClock ;
2023-04-02 10:01:57 -04:00
CHIP_VOL ( 29 , 0.4 ) ;
2023-04-02 01:32:47 -04:00
willExport [ i ] = true ;
writeK053260 [ 0 ] = disCont [ i ] . dispatch ;
} else if ( ! ( hasK053260 & 0x40000000 ) ) {
isSecond [ i ] = true ;
2023-04-02 10:01:57 -04:00
CHIP_VOL_SECOND ( 29 , 0.4 ) ;
2023-04-02 01:32:47 -04:00
willExport [ i ] = true ;
writeK053260 [ 1 ] = disCont [ i ] . dispatch ;
hasK053260 | = 0x40000000 ;
howManyChips + + ;
}
break ;
2022-10-16 02:27:39 -04:00
case DIV_SYSTEM_T6W28 :
if ( ! hasSN ) {
hasSN = 0xc0000000 | disCont [ i ] . dispatch - > chipClock ;
2023-01-15 03:54:11 -05:00
CHIP_VOL ( 0 , 2.0 ) ;
2022-10-16 02:29:02 -04:00
snNoiseConfig = 3 ;
snNoiseSize = 15 ;
2022-10-16 02:27:39 -04:00
willExport [ i ] = true ;
}
break ;
2023-08-09 07:30:00 -04:00
case DIV_SYSTEM_C140 :
2023-08-28 19:21:49 -04:00
if ( ! hasC140 ) {
2023-08-12 05:33:33 -04:00
// ?!?!?!
2023-08-28 19:21:49 -04:00
hasC140 = disCont [ i ] . dispatch - > rate / 2 ;
2023-08-12 18:08:01 -04:00
CHIP_VOL ( 40 , 1.0 ) ;
2023-08-09 07:30:00 -04:00
willExport [ i ] = true ;
writeC140 [ 0 ] = disCont [ i ] . dispatch ;
2023-10-11 03:48:39 -04:00
c140Type = ( song . systemFlags [ i ] . getInt ( " bankType " , 0 ) = = 1 ) ? 1 : 0 ;
2023-08-28 19:21:49 -04:00
} else if ( ! ( hasC140 & 0x40000000 ) ) {
2023-08-09 07:30:00 -04:00
isSecond [ i ] = true ;
2023-08-12 18:08:01 -04:00
CHIP_VOL_SECOND ( 40 , 1.0 ) ;
2023-08-09 07:30:00 -04:00
willExport [ i ] = true ;
writeC140 [ 1 ] = disCont [ i ] . dispatch ;
2023-08-28 19:21:49 -04:00
hasC140 | = 0x40000000 ;
howManyChips + + ;
}
break ;
case DIV_SYSTEM_C219 :
if ( ! hasC140 ) {
// ?!?!?!
hasC140 = disCont [ i ] . dispatch - > rate / 2 ;
CHIP_VOL ( 40 , 1.0 ) ;
willExport [ i ] = true ;
writeC219 [ 0 ] = disCont [ i ] . dispatch ;
c140Type = 2 ;
} else if ( ! ( hasC140 & 0x40000000 ) ) {
isSecond [ i ] = true ;
CHIP_VOL_SECOND ( 40 , 1.0 ) ;
willExport [ i ] = true ;
writeC219 [ 1 ] = disCont [ i ] . dispatch ;
hasC140 | = 0x40000000 ;
c140Type = 2 ;
2023-08-09 07:30:00 -04:00
howManyChips + + ;
}
break ;
2024-07-13 00:15:40 -04:00
case DIV_SYSTEM_OPL4 :
case DIV_SYSTEM_OPL4_DRUMS :
if ( ! hasOPL4 ) {
hasOPL4 = disCont [ i ] . dispatch - > chipClock ;
2024-07-13 03:28:20 -04:00
CHIP_VOL ( 13 , 1.0 ) ;
2024-07-13 00:15:40 -04:00
willExport [ i ] = true ;
writePCM_OPL4 [ 0 ] = disCont [ i ] . dispatch ;
} else if ( ! ( hasOPL4 & 0x40000000 ) ) {
isSecond [ i ] = true ;
2024-07-13 03:28:20 -04:00
CHIP_VOL_SECOND ( 13 , 1.0 ) ;
2024-07-13 00:15:40 -04:00
willExport [ i ] = true ;
writePCM_OPL4 [ 1 ] = disCont [ i ] . dispatch ;
hasOPL4 | = 0x40000000 ;
howManyChips + + ;
}
break ;
2022-02-18 12:58:36 -05:00
default :
break ;
}
if ( willExport [ i ] ) {
disCont [ i ] . dispatch - > toggleRegisterDump ( true ) ;
}
}
2023-05-24 03:12:42 -04:00
// variable set but not used?
logV ( " howManyChips: %d " , howManyChips ) ;
2022-02-18 12:58:36 -05:00
// write chips and stuff
w - > writeI ( hasSN ) ;
w - > writeI ( hasOPLL ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ; // length. will be written later
w - > writeI ( 0 ) ; // loop. will be written later
w - > writeI ( 0 ) ; // loop length. why is this necessary?
w - > writeI ( 0 ) ; // tick rate
w - > writeS ( snNoiseConfig ) ;
w - > writeC ( snNoiseSize ) ;
2022-04-01 22:40:32 -04:00
if ( version > = 0x151 ) {
w - > writeC ( snFlags ) ;
} else {
w - > writeC ( 0 ) ;
}
2022-02-18 12:58:36 -05:00
w - > writeI ( hasOPN2 ) ;
w - > writeI ( hasOPM ) ;
w - > writeI ( 0 ) ; // data pointer. will be written later
2022-04-01 22:40:32 -04:00
if ( version > = 0x151 ) {
w - > writeI ( hasSegaPCM ) ;
w - > writeI ( segaPCMOffset ) ;
w - > writeI ( hasRFC ) ;
w - > writeI ( hasOPN ) ;
w - > writeI ( hasOPNA ) ;
w - > writeI ( hasOPNB ) ;
w - > writeI ( hasOPL2 ) ;
w - > writeI ( hasOPL ) ;
w - > writeI ( hasY8950 ) ;
w - > writeI ( hasOPL3 ) ;
w - > writeI ( hasOPL4 ) ;
w - > writeI ( hasOPX ) ;
w - > writeI ( hasZ280 ) ;
w - > writeI ( hasRFC1 ) ;
w - > writeI ( hasPWM ) ;
w - > writeI ( hasAY ) ;
w - > writeC ( ayConfig ) ;
w - > writeC ( ayFlags ) ;
w - > writeC ( ayFlags ) ; // OPN
w - > writeC ( ayFlags ) ; // OPNA
} else {
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0 ) ; // OPN
w - > writeC ( 0 ) ; // OPNA
}
2023-01-15 03:54:11 -05:00
if ( version > = 0x160 ) { // global volume
double abnormalVol = song . masterVol * ( double ) chipVolSum / ( 256.0 * MAX ( 1 , chipAccounting ) ) ;
int calcVolume = 32.0 * ( log ( abnormalVol ) / log ( 2.0 ) ) ;
2022-10-09 18:24:24 -04:00
if ( calcVolume < - 63 ) calcVolume = - 63 ;
if ( calcVolume > 192 ) calcVolume = 192 ;
w - > writeC ( calcVolume & 0xff ) ; // volume
} else {
w - > writeC ( 0 ) ; // volume
}
2022-04-01 22:40:32 -04:00
// currently not used but is part of 1.60
2022-02-18 12:58:36 -05:00
w - > writeC ( 0 ) ; // reserved
w - > writeC ( 0 ) ; // loop count
2022-04-01 22:40:32 -04:00
// 1.51
2022-02-18 12:58:36 -05:00
w - > writeC ( 0 ) ; // loop modifier
2022-04-01 22:40:32 -04:00
if ( version > = 0x161 ) {
w - > writeI ( hasGB ) ;
w - > writeI ( hasNES ) ;
w - > writeI ( hasMultiPCM ) ;
w - > writeI ( hasuPD7759 ) ;
w - > writeI ( hasOKIM6258 ) ;
2023-09-11 02:19:02 -04:00
w - > writeC ( hasOKIM6258 ? 10 : 0 ) ; // flags
2022-04-01 22:40:32 -04:00
w - > writeC ( 0 ) ; // K flags
2023-08-28 19:21:49 -04:00
w - > writeC ( c140Type ) ; // C140 chip type
2022-04-01 22:40:32 -04:00
w - > writeC ( 0 ) ; // reserved
w - > writeI ( hasOKIM6295 ) ;
w - > writeI ( hasK051649 ) ;
w - > writeI ( hasK054539 ) ;
w - > writeI ( hasPCE ) ;
2023-08-28 19:21:49 -04:00
w - > writeI ( hasC140 ) ;
2022-04-01 22:40:32 -04:00
w - > writeI ( hasK053260 ) ;
w - > writeI ( hasPOKEY ) ;
w - > writeI ( hasQSound ) ;
} else {
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeC ( 0 ) ; // flags
w - > writeC ( 0 ) ; // K flags
w - > writeC ( 0 ) ; // C140 chip type
w - > writeC ( 0 ) ; // reserved
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
}
if ( version > = 0x171 ) {
w - > writeI ( hasSCSP ) ;
} else {
w - > writeI ( 0 ) ;
}
// 1.70
2022-02-18 12:58:36 -05:00
w - > writeI ( 0 ) ; // extra header
2022-04-01 22:40:32 -04:00
// 1.71
if ( version > = 0x171 ) {
w - > writeI ( hasSwan ) ;
w - > writeI ( hasVSU ) ;
w - > writeI ( hasSAA ) ;
w - > writeI ( hasES5503 ) ;
w - > writeI ( hasES5505 ) ;
w - > writeC ( 0 ) ; // 5503 chans
2022-04-20 12:52:37 -04:00
w - > writeC ( hasES5505 ? 1 : 0 ) ; // 5505 chans
2022-04-01 22:40:32 -04:00
w - > writeC ( 0 ) ; // C352 clock divider
w - > writeC ( 0 ) ; // reserved
w - > writeI ( hasX1 ) ;
w - > writeI ( hasC352 ) ;
w - > writeI ( hasGA20 ) ;
2023-01-18 04:32:03 -05:00
w - > writeI ( version > = 0x172 ? hasLynx : 0 ) ; //Mikey introduced in 1.72
2022-04-01 22:40:32 -04:00
} else {
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeC ( 0 ) ; // 5503 chans
w - > writeC ( 0 ) ; // 5505 chans
w - > writeC ( 0 ) ; // C352 clock divider
w - > writeC ( 0 ) ; // reserved
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
}
2022-02-20 12:15:15 -05:00
for ( int i = 0 ; i < 6 ; i + + ) { // reserved
2022-02-18 12:58:36 -05:00
w - > writeI ( 0 ) ;
}
unsigned int exHeaderOff = w - > tell ( ) ;
2022-10-09 18:24:24 -04:00
if ( version > = 0x170 ) {
logD ( " writing extended header... " ) ;
2022-10-19 14:48:29 -04:00
w - > writeI ( 12 ) ;
2022-10-09 18:24:24 -04:00
w - > writeI ( 0 ) ;
2022-02-18 12:58:36 -05:00
w - > writeI ( 4 ) ;
2022-10-09 18:24:24 -04:00
// write chip volumes
logD ( " writing chip volumes (%ld)... " , chipVol . size ( ) ) ;
w - > writeC ( chipVol . size ( ) ) ;
for ( unsigned int & i : chipVol ) {
logV ( " - %.8x " , i ) ;
w - > writeI ( i ) ;
}
}
2022-02-18 12:58:36 -05:00
unsigned int songOff = w - > tell ( ) ;
2022-09-25 21:07:21 -04:00
// initialize sample offsets
2025-07-26 19:48:23 -04:00
memset ( sampleOff8 , 0 , 32768 * sizeof ( unsigned int ) ) ;
memset ( sampleLen8 , 0 , 32768 * sizeof ( unsigned int ) ) ;
memset ( sampleOffSegaPCM , 0 , 32768 * sizeof ( unsigned int ) ) ;
2022-09-25 21:07:21 -04:00
2022-02-18 12:58:36 -05:00
// write samples
unsigned int sampleSeek = 0 ;
for ( int i = 0 ; i < song . sampleLen ; i + + ) {
DivSample * sample = song . sample [ i ] ;
2022-04-10 23:12:02 -04:00
logI ( " setting seek to %d " , sampleSeek ) ;
2022-09-25 21:07:21 -04:00
sampleOff8 [ i ] = sampleSeek ;
2023-07-09 18:41:24 -04:00
sampleLen8 [ i ] = sample - > length8 ;
2022-02-24 03:57:45 -05:00
sampleSeek + = sample - > length8 ;
2022-02-18 12:58:36 -05:00
}
2022-10-17 05:17:00 -04:00
if ( writeDACSamples & & ! directStream ) for ( int i = 0 ; i < song . sampleLen ; i + + ) {
2022-02-18 12:58:36 -05:00
DivSample * sample = song . sample [ i ] ;
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0 ) ;
2022-02-24 03:57:45 -05:00
w - > writeI ( sample - > length8 ) ;
for ( unsigned int j = 0 ; j < sample - > length8 ; j + + ) {
2024-12-15 15:56:18 -05:00
w - > writeC ( ( unsigned char ) sample - > data8 [ j ] ^ 0x80 ) ;
2022-02-18 12:58:36 -05:00
}
}
2022-10-17 05:17:00 -04:00
if ( writeNESSamples & & ! directStream ) for ( int i = 0 ; i < song . sampleLen ; i + + ) {
2022-02-18 12:58:36 -05:00
DivSample * sample = song . sample [ i ] ;
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 7 ) ;
2022-02-24 03:57:45 -05:00
w - > writeI ( sample - > length8 ) ;
for ( unsigned int j = 0 ; j < sample - > length8 ; j + + ) {
2024-12-15 15:56:18 -05:00
w - > writeC ( ( ( unsigned char ) sample - > data8 [ j ] ^ 0x80 ) > > 1 ) ;
2022-02-18 12:58:36 -05:00
}
2023-06-22 05:55:12 -04:00
bankOffsetNESCurrent + = sample - > length8 ;
2022-02-18 12:58:36 -05:00
}
2022-10-17 05:17:00 -04:00
if ( writePCESamples & & ! directStream ) for ( int i = 0 ; i < song . sampleLen ; i + + ) {
2022-02-18 12:58:36 -05:00
DivSample * sample = song . sample [ i ] ;
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 5 ) ;
2022-02-24 03:57:45 -05:00
w - > writeI ( sample - > length8 ) ;
for ( unsigned int j = 0 ; j < sample - > length8 ; j + + ) {
2024-12-15 15:56:18 -05:00
w - > writeC ( ( ( unsigned char ) sample - > data8 [ j ] ^ 0x80 ) > > 3 ) ;
2022-02-18 12:58:36 -05:00
}
}
2023-09-11 02:19:02 -04:00
if ( writeVOXSamples & & ! directStream ) for ( int i = 0 ; i < song . sampleLen ; i + + ) {
DivSample * sample = song . sample [ i ] ;
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 4 ) ;
w - > writeI ( sample - > lengthVOX ) ;
for ( unsigned int j = 0 ; j < sample - > lengthVOX ; j + + ) {
unsigned char actualData = ( sample - > dataVOX [ j ] > > 4 ) | ( sample - > dataVOX [ j ] < < 4 ) ;
w - > writeC ( actualData ) ;
}
}
2024-10-17 16:06:13 -04:00
if ( writeLynxSamples & & ! directStream ) for ( int i = 0 ; i < song . sampleLen ; i + + ) {
DivSample * sample = song . sample [ i ] ;
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 8 ) ;
w - > writeI ( sample - > length8 ) ;
for ( unsigned int j = 0 ; j < sample - > length8 ; j + + ) {
w - > writeC ( sample - > data8 [ j ] ) ;
}
}
2023-02-10 02:01:23 -05:00
for ( int i = 0 ; i < 2 ; i + + ) {
// SegaPCM
if ( writeSegaPCM [ i ] ! = NULL & & writeSegaPCM [ i ] - > getSampleMemUsage ( 0 ) > 0 ) {
2022-04-16 11:54:01 -04:00
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
2023-02-10 02:47:26 -05:00
w - > writeC ( 0x80 ) ;
2023-02-10 02:01:23 -05:00
w - > writeI ( ( writeSegaPCM [ i ] - > getSampleMemUsage ( 0 ) + 8 ) | ( i * 0x80000000 ) ) ;
w - > writeI ( writeSegaPCM [ i ] - > getSampleMemCapacity ( 0 ) ) ;
2022-04-16 11:54:01 -04:00
w - > writeI ( 0 ) ;
2023-02-10 02:01:23 -05:00
w - > write ( writeSegaPCM [ i ] - > getSampleMem ( 0 ) , writeSegaPCM [ i ] - > getSampleMemUsage ( 0 ) ) ;
2022-04-16 11:54:01 -04:00
}
2022-05-20 15:34:51 -04:00
// ADPCM (OPNA)
2022-05-19 19:09:46 -04:00
if ( writeADPCM_OPNA [ i ] ! = NULL & & writeADPCM_OPNA [ i ] - > getSampleMemUsage ( 0 ) > 0 ) {
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0x81 ) ;
w - > writeI ( ( writeADPCM_OPNA [ i ] - > getSampleMemUsage ( 0 ) + 8 ) | ( i * 0x80000000 ) ) ;
w - > writeI ( writeADPCM_OPNA [ i ] - > getSampleMemCapacity ( 0 ) ) ;
w - > writeI ( 0 ) ;
w - > write ( writeADPCM_OPNA [ i ] - > getSampleMem ( 0 ) , writeADPCM_OPNA [ i ] - > getSampleMemUsage ( 0 ) ) ;
}
2022-05-20 15:34:51 -04:00
// ADPCM-A (OPNB)
2022-05-19 19:09:46 -04:00
if ( writeADPCM_OPNB [ i ] ! = NULL & & writeADPCM_OPNB [ i ] - > getSampleMemUsage ( 0 ) > 0 ) {
2022-04-16 11:54:01 -04:00
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0x82 ) ;
2022-05-19 19:09:46 -04:00
w - > writeI ( ( writeADPCM_OPNB [ i ] - > getSampleMemUsage ( 0 ) + 8 ) | ( i * 0x80000000 ) ) ;
w - > writeI ( writeADPCM_OPNB [ i ] - > getSampleMemCapacity ( 0 ) ) ;
2022-04-16 11:54:01 -04:00
w - > writeI ( 0 ) ;
2022-05-19 19:09:46 -04:00
w - > write ( writeADPCM_OPNB [ i ] - > getSampleMem ( 0 ) , writeADPCM_OPNB [ i ] - > getSampleMemUsage ( 0 ) ) ;
2022-04-16 11:54:01 -04:00
}
2022-05-20 15:34:51 -04:00
// ADPCM-B (OPNB)
2022-05-19 19:09:46 -04:00
if ( writeADPCM_OPNB [ i ] ! = NULL & & writeADPCM_OPNB [ i ] - > getSampleMemUsage ( 1 ) > 0 ) {
2022-04-16 11:54:01 -04:00
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0x83 ) ;
2022-05-19 19:09:46 -04:00
w - > writeI ( ( writeADPCM_OPNB [ i ] - > getSampleMemUsage ( 1 ) + 8 ) | ( i * 0x80000000 ) ) ;
w - > writeI ( writeADPCM_OPNB [ i ] - > getSampleMemCapacity ( 1 ) ) ;
2022-04-16 11:54:01 -04:00
w - > writeI ( 0 ) ;
2022-05-19 19:09:46 -04:00
w - > write ( writeADPCM_OPNB [ i ] - > getSampleMem ( 1 ) , writeADPCM_OPNB [ i ] - > getSampleMemUsage ( 1 ) ) ;
}
2022-05-20 15:34:51 -04:00
// ADPCM (Y8950)
2022-05-19 19:09:46 -04:00
if ( writeADPCM_Y8950 [ i ] ! = NULL & & writeADPCM_Y8950 [ i ] - > getSampleMemUsage ( 0 ) > 0 ) {
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0x88 ) ;
w - > writeI ( ( writeADPCM_Y8950 [ i ] - > getSampleMemUsage ( 0 ) + 8 ) | ( i * 0x80000000 ) ) ;
w - > writeI ( writeADPCM_Y8950 [ i ] - > getSampleMemCapacity ( 0 ) ) ;
2022-04-16 11:54:01 -04:00
w - > writeI ( 0 ) ;
2022-05-19 19:09:46 -04:00
w - > write ( writeADPCM_Y8950 [ i ] - > getSampleMem ( 0 ) , writeADPCM_Y8950 [ i ] - > getSampleMemUsage ( 0 ) ) ;
2022-04-16 11:54:01 -04:00
}
2024-09-25 14:12:34 -04:00
// QSound has two sample types sharing the same memory.
// however, they are prepresented as separate memories in Furnace.
// we find the largest one to see how much memory is being used in total.
if ( writeQSound [ i ] ! = NULL & & ( writeQSound [ i ] - > getSampleMemUsage ( 0 ) > 0 | | writeQSound [ i ] - > getSampleMemUsage ( 1 ) > 0 ) ) {
unsigned int blockSize = ( writeQSound [ i ] - > getSampleMemUsage ( 0 ) + writeQSound [ i ] - > getSampleMemUsage ( 1 ) + 0xffff ) & ( ~ 0xffff ) ;
2022-05-01 13:57:44 -04:00
if ( blockSize > 0x1000000 ) {
blockSize = 0x1000000 ;
}
2022-04-16 11:54:01 -04:00
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0x8F ) ;
w - > writeI ( ( blockSize + 8 ) | ( i * 0x80000000 ) ) ;
2024-09-25 14:12:34 -04:00
w - > writeI ( writeQSound [ i ] - > getSampleMemCapacity ( 0 ) ) ;
2022-04-16 11:54:01 -04:00
w - > writeI ( 0 ) ;
2022-05-01 13:57:44 -04:00
w - > write ( writeQSound [ i ] - > getSampleMem ( ) , blockSize ) ;
2022-04-16 11:54:01 -04:00
}
2022-05-01 13:57:44 -04:00
if ( writeX1010 [ i ] ! = NULL & & writeX1010 [ i ] - > getSampleMemUsage ( ) > 0 ) {
2022-04-16 11:54:01 -04:00
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0x91 ) ;
2022-05-01 13:57:44 -04:00
w - > writeI ( ( writeX1010 [ i ] - > getSampleMemUsage ( ) + 8 ) | ( i * 0x80000000 ) ) ;
w - > writeI ( writeX1010 [ i ] - > getSampleMemCapacity ( ) ) ;
2022-04-16 11:54:01 -04:00
w - > writeI ( 0 ) ;
2022-05-01 13:57:44 -04:00
w - > write ( writeX1010 [ i ] - > getSampleMem ( ) , writeX1010 [ i ] - > getSampleMemUsage ( ) ) ;
2022-04-16 11:54:01 -04:00
}
2022-05-18 02:55:33 -04:00
if ( writeZ280 [ i ] ! = NULL & & writeZ280 [ i ] - > getSampleMemUsage ( ) > 0 ) {
2022-06-22 10:55:31 -04:00
// In VGM, YMZ280B's 16-bit PCM has an endianness swapped
// which have been fixed in the upstream MAME since 2013
// in order to get Konami FireBeat working
// The reason given for VGM not applying this change was
// "It matches OPL4 and MAME probably did an endianness optimization"
size_t sampleMemLen = writeZ280 [ i ] - > getSampleMemUsage ( ) ;
unsigned char * sampleMem = new unsigned char [ sampleMemLen ] ;
memcpy ( sampleMem , writeZ280 [ i ] - > getSampleMem ( ) , sampleMemLen ) ;
2022-05-18 02:55:33 -04:00
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0x86 ) ;
w - > writeI ( ( writeZ280 [ i ] - > getSampleMemUsage ( ) + 8 ) | ( i * 0x80000000 ) ) ;
w - > writeI ( writeZ280 [ i ] - > getSampleMemCapacity ( ) ) ;
w - > writeI ( 0 ) ;
2022-06-22 10:55:31 -04:00
w - > write ( sampleMem , sampleMemLen ) ;
delete [ ] sampleMem ;
2022-05-18 02:55:33 -04:00
}
2024-07-13 00:15:40 -04:00
// PCM (OPL4)
if ( writePCM_OPL4 [ i ] ! = NULL & & writePCM_OPL4 [ i ] - > getSampleMemUsage ( 0 ) > 0 ) {
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0x84 ) ;
w - > writeI ( ( writePCM_OPL4 [ i ] - > getSampleMemUsage ( 0 ) + 8 ) | ( i * 0x80000000 ) ) ;
w - > writeI ( writePCM_OPL4 [ i ] - > getSampleMemCapacity ( 0 ) ) ;
w - > writeI ( 0 ) ;
w - > write ( writePCM_OPL4 [ i ] - > getSampleMem ( 0 ) , writePCM_OPL4 [ i ] - > getSampleMemUsage ( 0 ) ) ;
}
2022-03-06 12:31:03 -05:00
}
2022-05-20 14:45:26 -04:00
for ( int i = 0 ; i < 2 ; i + + ) {
if ( writeRF5C68 [ i ] ! = NULL & & writeRF5C68 [ i ] - > getSampleMemUsage ( ) > 0 ) {
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0xc0 + i ) ;
2023-07-09 19:46:24 -04:00
w - > writeI ( writeRF5C68 [ i ] - > getSampleMemUsage ( ) + 2 ) ;
w - > writeS ( 0 ) ;
2022-05-20 14:45:26 -04:00
w - > write ( writeRF5C68 [ i ] - > getSampleMem ( ) , writeRF5C68 [ i ] - > getSampleMemUsage ( ) ) ;
}
2022-05-28 19:15:26 -04:00
if ( writeMSM6295 [ i ] ! = NULL & & writeMSM6295 [ i ] - > getSampleMemUsage ( ) > 0 ) {
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0x8b ) ;
w - > writeI ( ( writeMSM6295 [ i ] - > getSampleMemUsage ( ) + 8 ) | ( i * 0x80000000 ) ) ;
w - > writeI ( writeMSM6295 [ i ] - > getSampleMemCapacity ( ) ) ;
w - > writeI ( 0 ) ;
w - > write ( writeMSM6295 [ i ] - > getSampleMem ( ) , writeMSM6295 [ i ] - > getSampleMemUsage ( ) ) ;
}
2022-12-15 07:36:51 -05:00
if ( writeGA20 [ i ] ! = NULL & & writeGA20 [ i ] - > getSampleMemUsage ( ) > 0 ) {
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0x93 ) ;
w - > writeI ( ( writeGA20 [ i ] - > getSampleMemUsage ( ) + 8 ) | ( i * 0x80000000 ) ) ;
w - > writeI ( writeGA20 [ i ] - > getSampleMemCapacity ( ) ) ;
w - > writeI ( 0 ) ;
w - > write ( writeGA20 [ i ] - > getSampleMem ( ) , writeGA20 [ i ] - > getSampleMemUsage ( ) ) ;
}
2023-04-02 01:32:47 -04:00
if ( writeK053260 [ i ] ! = NULL & & writeK053260 [ i ] - > getSampleMemUsage ( ) > 0 ) {
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0x8e ) ;
w - > writeI ( ( writeK053260 [ i ] - > getSampleMemUsage ( ) + 8 ) | ( i * 0x80000000 ) ) ;
w - > writeI ( writeK053260 [ i ] - > getSampleMemCapacity ( ) ) ;
w - > writeI ( 0 ) ;
w - > write ( writeK053260 [ i ] - > getSampleMem ( ) , writeK053260 [ i ] - > getSampleMemUsage ( ) ) ;
}
2023-06-22 04:24:39 -04:00
if ( writeNES [ i ] ! = NULL & & writeNES [ i ] - > getSampleMemUsage ( ) > 0 ) {
2025-05-06 16:19:59 -04:00
if ( dpcm07 ) {
size_t howMuchWillBeWritten = writeNES [ i ] - > getSampleMemUsage ( ) ;
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 7 ) ;
w - > writeI ( howMuchWillBeWritten ) ;
w - > write ( writeNES [ i ] - > getSampleMem ( ) , howMuchWillBeWritten ) ;
bankOffsetNES [ i ] = bankOffsetNESCurrent ;
bankOffset [ writeNESIndex [ i ] ] = bankOffsetNES [ i ] ;
bankOffsetNESCurrent + = howMuchWillBeWritten ;
// force the first bank
w - > writeC ( 0x68 ) ;
w - > writeC ( 0x6c ) ;
w - > writeC ( 0x07 | ( i ? 0x80 : 0x00 ) ) ;
w - > writeC ( bankOffsetNES [ i ] & 0xff ) ;
w - > writeC ( ( bankOffsetNES [ i ] > > 8 ) & 0xff ) ;
w - > writeC ( ( bankOffsetNES [ i ] > > 16 ) & 0xff ) ;
w - > writeC ( 0x00 ) ;
w - > writeC ( 0xc0 ) ;
w - > writeC ( 0x00 ) ;
w - > writeC ( 0x00 ) ;
w - > writeC ( 0x40 ) ;
w - > writeC ( 0x00 ) ;
} else {
// write the first bank
size_t howMuchWillBeWritten = writeNES [ i ] - > getSampleMemUsage ( ) ;
if ( howMuchWillBeWritten > 16384 ) howMuchWillBeWritten = 16384 ;
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0xc2 ) ;
2025-05-07 15:40:02 -04:00
w - > writeI ( ( i ? 0x80000000 : 0 ) | ( howMuchWillBeWritten + 2 ) ) ;
2025-05-06 16:19:59 -04:00
// data
w - > writeS ( 0xc000 ) ;
w - > write ( writeNES [ i ] - > getSampleMem ( ) , howMuchWillBeWritten ) ;
}
2023-06-22 04:24:39 -04:00
}
2022-03-06 12:31:03 -05:00
}
2022-05-02 22:07:50 -04:00
// TODO
for ( int i = 0 ; i < 2 ; i + + ) {
if ( writeES5506 [ i ] ! = NULL & & writeES5506 [ i ] - > getSampleMemUsage ( ) > 0 ) {
2025-06-01 06:19:42 -04:00
// split sample data into 4 areas
unsigned short * mem = ( unsigned short * ) writeES5506 [ i ] - > getSampleMem ( ) ;
for ( int b = 0 ; b < 4 ; b + + ) {
int offs = b < < 22 ;
int memLen = CLAMP ( ( int ) writeES5506 [ i ] - > getSampleMemUsage ( ) - offs , 0 , 0x400000 ) ;
if ( memLen > 0 ) {
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0x90 ) ;
w - > writeI ( ( memLen + 8 ) | ( i * 0x80000000 ) ) ;
w - > writeI ( MIN ( writeES5506 [ i ] - > getSampleMemCapacity ( ) , 0x400000 ) ) ;
w - > writeI ( b < < 28 ) ;
for ( int i = 0 ; i < ( memLen > > 1 ) ; i + + ) {
w - > writeS ( mem [ ( offs > > 1 ) + i ] ) ;
}
}
}
2022-04-25 23:04:23 -04:00
}
2023-08-09 07:30:00 -04:00
if ( writeC140 [ i ] ! = NULL & & writeC140 [ i ] - > getSampleMemUsage ( ) > 0 ) {
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0x8d ) ;
2023-08-12 17:46:35 -04:00
unsigned short * mem = ( unsigned short * ) writeC140 [ i ] - > getSampleMem ( ) ;
size_t memLen = writeC140 [ i ] - > getSampleMemUsage ( ) > > 1 ;
w - > writeI ( ( memLen + 8 ) | ( i * 0x80000000 ) ) ;
2023-08-09 07:30:00 -04:00
w - > writeI ( writeC140 [ i ] - > getSampleMemCapacity ( ) ) ;
w - > writeI ( 0 ) ;
2023-08-12 17:46:35 -04:00
for ( size_t i = 0 ; i < memLen ; i + + ) {
w - > writeC ( mem [ i ] > > 8 ) ;
}
2023-08-09 07:30:00 -04:00
}
2023-08-28 19:21:49 -04:00
if ( writeC219 [ i ] ! = NULL & & writeC219 [ i ] - > getSampleMemUsage ( ) > 0 ) {
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0x8d ) ;
2023-08-29 04:42:16 -04:00
unsigned char * mem = ( unsigned char * ) writeC219 [ i ] - > getSampleMem ( ) ;
2023-08-28 19:21:49 -04:00
size_t memLen = writeC219 [ i ] - > getSampleMemUsage ( ) ;
w - > writeI ( ( memLen + 8 ) | ( i * 0x80000000 ) ) ;
w - > writeI ( writeC219 [ i ] - > getSampleMemCapacity ( ) ) ;
w - > writeI ( 0 ) ;
for ( size_t i = 0 ; i < memLen ; i + + ) {
w - > writeC ( mem [ i ] ) ;
}
}
2022-04-20 12:52:37 -04:00
}
2022-02-18 12:58:36 -05:00
// initialize streams
int streamID = 0 ;
2022-10-17 05:17:00 -04:00
if ( ! directStream ) {
for ( int i = 0 ; i < song . systemLen ; i + + ) {
if ( ! willExport [ i ] ) continue ;
streamIDs [ i ] = streamID ;
switch ( song . system [ i ] ) {
case DIV_SYSTEM_YM2612 :
case DIV_SYSTEM_YM2612_EXT :
2022-12-20 17:49:53 -05:00
case DIV_SYSTEM_YM2612_DUALPCM :
case DIV_SYSTEM_YM2612_DUALPCM_EXT :
2024-02-29 21:33:40 -05:00
case DIV_SYSTEM_YM2612_CSM :
2022-10-17 05:17:00 -04:00
w - > writeC ( 0x90 ) ;
w - > writeC ( streamID ) ;
w - > writeC ( 0x02 ) ;
w - > writeC ( 0 ) ; // port
w - > writeC ( 0x2a ) ; // DAC
2022-02-18 12:58:36 -05:00
2022-10-17 05:17:00 -04:00
w - > writeC ( 0x91 ) ;
w - > writeC ( streamID ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 1 ) ;
w - > writeC ( 0 ) ;
2022-02-18 12:58:36 -05:00
2022-10-17 05:17:00 -04:00
w - > writeC ( 0x92 ) ;
w - > writeC ( streamID ) ;
w - > writeI ( 32000 ) ; // default
streamID + + ;
break ;
case DIV_SYSTEM_NES :
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x90 ) ;
w - > writeC ( streamID ) ;
2022-10-17 05:17:00 -04:00
w - > writeC ( 20 ) ;
w - > writeC ( 0 ) ; // port
w - > writeC ( 0x11 ) ; // DAC
2022-02-18 12:58:36 -05:00
w - > writeC ( 0x91 ) ;
w - > writeC ( streamID ) ;
2022-10-17 05:17:00 -04:00
w - > writeC ( 7 ) ;
2022-02-18 12:58:36 -05:00
w - > writeC ( 1 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0x92 ) ;
w - > writeC ( streamID ) ;
2022-10-17 05:17:00 -04:00
w - > writeI ( 32000 ) ; // default
2022-02-18 12:58:36 -05:00
streamID + + ;
2022-10-17 05:17:00 -04:00
break ;
case DIV_SYSTEM_PCE :
for ( int j = 0 ; j < 6 ; j + + ) {
w - > writeC ( 0x90 ) ;
w - > writeC ( streamID ) ;
w - > writeC ( 27 ) ;
w - > writeC ( j ) ; // port
w - > writeC ( 0x06 ) ; // select+DAC
w - > writeC ( 0x91 ) ;
w - > writeC ( streamID ) ;
w - > writeC ( 5 ) ;
w - > writeC ( 1 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0x92 ) ;
w - > writeC ( streamID ) ;
w - > writeI ( 16000 ) ; // default
streamID + + ;
}
break ;
case DIV_SYSTEM_SWAN :
w - > writeC ( 0x90 ) ;
w - > writeC ( streamID ) ;
w - > writeC ( isSecond [ i ] ? 0xa1 : 0x21 ) ;
w - > writeC ( 0 ) ; // port
w - > writeC ( 0x09 ) ; // DAC
2022-03-06 11:13:47 -05:00
2022-10-17 05:17:00 -04:00
w - > writeC ( 0x91 ) ;
w - > writeC ( streamID ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 1 ) ;
w - > writeC ( 0 ) ;
2022-03-06 11:13:47 -05:00
2022-10-17 05:17:00 -04:00
w - > writeC ( 0x92 ) ;
w - > writeC ( streamID ) ;
w - > writeI ( 24000 ) ; // default
streamID + + ;
break ;
2023-09-11 02:19:02 -04:00
case DIV_SYSTEM_MSM6258 :
w - > writeC ( 0x90 ) ;
w - > writeC ( streamID ) ;
w - > writeC ( isSecond [ i ] ? 0x97 : 0x17 ) ;
w - > writeC ( 0 ) ; // port
w - > writeC ( 1 ) ; // data input
w - > writeC ( 0x91 ) ;
w - > writeC ( streamID ) ;
w - > writeC ( 4 ) ;
w - > writeC ( 1 ) ;
w - > writeC ( 0 ) ;
streamID + + ;
break ;
2024-10-17 05:04:00 -04:00
case DIV_SYSTEM_LYNX :
for ( int j = 0 ; j < 4 ; j + + ) {
w - > writeC ( 0x90 ) ;
w - > writeC ( streamID ) ;
w - > writeC ( isSecond [ i ] ? 0xa9 : 0x29 ) ;
w - > writeC ( 0 ) ; // port
w - > writeC ( 0x22 + ( j < < 3 ) ) ; // output write
w - > writeC ( 0x91 ) ;
w - > writeC ( streamID ) ;
2024-10-17 16:06:13 -04:00
w - > writeC ( 8 ) ;
2024-10-17 05:04:00 -04:00
w - > writeC ( 1 ) ;
w - > writeC ( 0 ) ;
w - > writeC ( 0x92 ) ;
w - > writeC ( streamID ) ;
w - > writeI ( 16000 ) ; // default
streamID + + ;
}
break ;
2022-10-17 05:17:00 -04:00
default :
break ;
}
2022-02-18 12:58:36 -05:00
}
}
// write song data
playSub ( false ) ;
size_t tickCount = 0 ;
bool writeLoop = false ;
2023-02-08 19:25:03 -05:00
bool alreadyWroteLoop = false ;
2022-08-03 19:44:45 -04:00
int ord = - 1 ;
int exportChans = 0 ;
for ( int i = 0 ; i < chans ; i + + ) {
if ( ! willExport [ dispatchOfChan [ i ] ] ) continue ;
exportChans + + ;
2023-02-08 19:25:03 -05:00
chan [ i ] . wentThroughNote = false ;
chan [ i ] . goneThroughNote = false ;
2022-08-03 19:44:45 -04:00
}
2022-02-18 12:58:36 -05:00
while ( ! done ) {
if ( loopPos = = - 1 ) {
2023-09-24 03:28:05 -04:00
if ( loopOrder = = curOrder & & loopRow = = curRow ) {
2024-03-15 15:56:55 -04:00
if ( ( ticks - ( ( tempoAccum + virtualTempoN ) / virtualTempoD ) ) < = 0 ) {
2023-09-24 03:28:05 -04:00
writeLoop = true ;
}
2022-02-18 12:58:36 -05:00
}
}
2023-02-08 19:25:03 -05:00
songTick + + ;
2023-02-08 18:04:09 -05:00
tickPos . push_back ( w - > tell ( ) ) ;
2023-02-08 19:25:03 -05:00
tickSample . push_back ( tickCount ) ;
if ( nextTick ( false , true ) ) {
if ( trailing ) beenOneLoopAlready = true ;
trailing = true ;
2023-05-24 15:25:30 -04:00
if ( ! loop ) countDown = 0 ;
2023-02-08 19:25:03 -05:00
for ( int i = 0 ; i < chans ; i + + ) {
if ( ! willExport [ dispatchOfChan [ i ] ] ) continue ;
chan [ i ] . wentThroughNote = false ;
}
}
if ( trailing ) {
switch ( trailingTicks ) {
case - 1 : { // automatic
bool stillHaveTo = false ;
for ( int i = 0 ; i < chans ; i + + ) {
if ( ! willExport [ dispatchOfChan [ i ] ] ) continue ;
if ( ! chan [ i ] . goneThroughNote ) continue ;
if ( ! chan [ i ] . wentThroughNote ) {
stillHaveTo = true ;
break ;
}
}
if ( ! stillHaveTo ) countDown = 0 ;
break ;
}
case - 2 : // one loop
break ;
default : // custom
countDown - - ;
break ;
}
if ( song . loopModality ! = 2 ) countDown = 0 ;
if ( countDown > 0 & & ! beenOneLoopAlready ) {
loopTickSong + + ;
}
}
if ( countDown < = 0 | | ! playing | | beenOneLoopAlready ) {
2022-02-18 12:58:36 -05:00
done = true ;
if ( ! loop ) {
for ( int i = 0 ; i < song . systemLen ; i + + ) {
disCont [ i ] . dispatch - > getRegisterWrites ( ) . clear ( ) ;
}
break ;
}
// stop all streams
2022-10-17 05:17:00 -04:00
if ( ! directStream ) {
for ( int i = 0 ; i < streamID ; i + + ) {
w - > writeC ( 0x94 ) ;
w - > writeC ( i ) ;
loopSample [ i ] = - 1 ;
}
2022-02-18 12:58:36 -05:00
}
2022-02-20 23:13:52 -05:00
if ( ! playing ) {
writeLoop = false ;
loopPos = - 1 ;
}
2022-08-03 19:44:45 -04:00
} else {
// check for pattern change
if ( prevOrder ! = ord ) {
logI ( " registering order change %d on %d " , prevOrder , prevRow ) ;
ord = prevOrder ;
if ( patternHints ) {
w - > writeC ( 0x67 ) ;
w - > writeC ( 0x66 ) ;
w - > writeC ( 0xfe ) ;
w - > writeI ( 3 + exportChans ) ;
w - > writeC ( 0x01 ) ;
w - > writeC ( prevOrder ) ;
w - > writeC ( prevRow ) ;
for ( int i = 0 ; i < chans ; i + + ) {
if ( ! willExport [ dispatchOfChan [ i ] ] ) continue ;
w - > writeC ( curSubSong - > orders . ord [ i ] [ prevOrder ] ) ;
}
}
}
2022-02-18 12:58:36 -05:00
}
2024-10-19 15:24:26 -04:00
2024-11-04 04:00:50 -05:00
auto runStreams = [ & ] ( int runTime , int & wtAccum ) - > int {
if ( ! directStream ) {
for ( int i = 0 ; i < streamID ; i + + ) {
if ( loopSample [ i ] > = 0 ) {
loopTimer [ i ] - = ( loopFreq [ i ] / 44100.0 ) * ( double ) runTime ;
}
}
bool haveNegatives = false ;
for ( int i = 0 ; i < streamID ; i + + ) {
if ( loopSample [ i ] > = 0 ) {
if ( loopTimer [ i ] < 0 ) {
haveNegatives = true ;
}
}
}
while ( haveNegatives ) {
// finish all negatives
int nextToTouch = - 1 ;
for ( int i = 0 ; i < streamID ; i + + ) {
if ( loopSample [ i ] > = 0 ) {
if ( loopTimer [ i ] < 0 ) {
if ( nextToTouch > = 0 ) {
if ( loopTimer [ nextToTouch ] > loopTimer [ i ] ) nextToTouch = i ;
} else {
nextToTouch = i ;
}
}
}
}
if ( nextToTouch > = 0 ) {
double waitTime = runTime + ( loopTimer [ nextToTouch ] * ( 44100.0 / MAX ( 1 , loopFreq [ nextToTouch ] ) ) ) ;
if ( waitTime > 0 ) {
w - > writeC ( 0x61 ) ;
w - > writeS ( waitTime ) ;
logV ( " wait is: %f " , waitTime ) ;
runTime - = waitTime ;
wtAccum + = waitTime ;
}
if ( loopSample [ nextToTouch ] < song . sampleLen ) {
DivSample * sample = song . sample [ loopSample [ nextToTouch ] ] ;
// insert loop
if ( sample - > getLoopStartPosition ( DIV_SAMPLE_DEPTH_8BIT ) < sample - > getLoopEndPosition ( DIV_SAMPLE_DEPTH_8BIT ) ) {
w - > writeC ( 0x93 ) ;
w - > writeC ( nextToTouch ) ;
w - > writeI ( sampleOff8 [ loopSample [ nextToTouch ] ] + sample - > getLoopStartPosition ( DIV_SAMPLE_DEPTH_8BIT ) ) ;
w - > writeC ( 0x81 ) ;
w - > writeI ( sample - > getLoopEndPosition ( DIV_SAMPLE_DEPTH_8BIT ) - sample - > getLoopStartPosition ( DIV_SAMPLE_DEPTH_8BIT ) ) ;
}
}
loopSample [ nextToTouch ] = - 1 ;
} else {
haveNegatives = false ;
}
}
}
return runTime ;
} ;
2024-10-19 15:24:26 -04:00
// calculate number of samples in this tick
2025-03-11 12:23:25 -04:00
int totalWait = cycles ;
2024-10-19 15:24:26 -04:00
// get register dumps and put them into delayed writes
int writeNum = 0 ;
2022-02-18 12:58:36 -05:00
for ( int i = 0 ; i < song . systemLen ; i + + ) {
2024-10-19 18:52:24 -04:00
int curDelay = 0 ;
2022-02-18 12:58:36 -05:00
std : : vector < DivRegWrite > & writes = disCont [ i ] . dispatch - > getRegisterWrites ( ) ;
for ( DivRegWrite & j : writes ) {
2024-10-19 18:52:24 -04:00
if ( j . addr = = 0xfffffffe ) { // delay
curDelay + = ( double ) j . val * ( 44100.0 / ( double ) disCont [ i ] . dispatch - > rate ) ;
if ( curDelay > totalWait ) curDelay = totalWait - 1 ;
} else {
sortedWrites . push_back ( std : : pair < int , DivDelayedWrite > ( i , DivDelayedWrite ( curDelay , writeNum + + , j . addr , j . val ) ) ) ;
}
2022-02-18 12:58:36 -05:00
}
writes . clear ( ) ;
}
2024-10-19 15:24:26 -04:00
// handle direct stream writes
2022-10-17 05:17:00 -04:00
if ( directStream ) {
2022-10-17 15:25:30 -04:00
// render stream of all chips
2022-10-17 05:17:00 -04:00
for ( int i = 0 ; i < song . systemLen ; i + + ) {
disCont [ i ] . dispatch - > fillStream ( delayedWrites [ i ] , 44100 , totalWait ) ;
2022-10-17 15:25:30 -04:00
for ( DivDelayedWrite & j : delayedWrites [ i ] ) {
sortedWrites . push_back ( std : : pair < int , DivDelayedWrite > ( i , j ) ) ;
}
delayedWrites [ i ] . clear ( ) ;
2022-02-18 12:58:36 -05:00
}
2024-10-19 15:24:26 -04:00
}
2022-10-17 15:25:30 -04:00
2024-10-19 15:24:26 -04:00
// put writes
if ( ! sortedWrites . empty ( ) ) {
// sort writes
std : : sort ( sortedWrites . begin ( ) , sortedWrites . end ( ) , [ ] ( const std : : pair < int , DivDelayedWrite > & a , const std : : pair < int , DivDelayedWrite > & b ) - > bool {
if ( a . second . time = = b . second . time ) {
return a . second . order < b . second . order ;
}
return a . second . time < b . second . time ;
} ) ;
// write it out
int lastOne = 0 ;
for ( std : : pair < int , DivDelayedWrite > & i : sortedWrites ) {
if ( i . second . time > lastOne ) {
// write delay
int delay = i . second . time - lastOne ;
2024-11-04 04:00:50 -05:00
// handle streams
int wtAccum1 = 0 ;
delay = runStreams ( delay , wtAccum1 ) ;
// ????
2024-10-19 15:24:26 -04:00
if ( delay > 16 ) {
w - > writeC ( 0x61 ) ;
w - > writeS ( delay ) ;
} else if ( delay > 0 ) {
w - > writeC ( 0x70 + delay - 1 ) ;
2022-10-17 15:25:30 -04:00
}
2024-10-19 15:24:26 -04:00
lastOne = i . second . time ;
2022-02-18 12:58:36 -05:00
}
2024-10-19 15:24:26 -04:00
// write write
2025-06-06 03:45:59 -04:00
performVGMWrite ( w , song . system [ i . first ] , i . second . write , streamIDs [ i . first ] , loopTimer , loopFreq , loopSample , sampleDir , isSecond [ i . first ] , pendingFreq , playingSample , setPos , sampleOff8 , sampleLen8 , bankOffset [ i . first ] , directStream , sampleStoppable , dpcm07 , writeNES , correctedRate ) ;
2024-10-19 15:24:26 -04:00
writeCount + + ;
2022-02-18 12:58:36 -05:00
}
2024-10-19 15:24:26 -04:00
sortedWrites . clear ( ) ;
totalWait - = lastOne ;
tickCount + = lastOne ;
}
// handle streams
2024-11-04 04:00:50 -05:00
int wtAccum = 0 ;
totalWait = runStreams ( totalWait , wtAccum ) ;
tickCount + = wtAccum ;
2024-10-19 15:24:26 -04:00
2022-02-18 12:58:36 -05:00
// write wait
if ( totalWait > 0 ) {
if ( totalWait = = 735 ) {
w - > writeC ( 0x62 ) ;
} else if ( totalWait = = 882 ) {
w - > writeC ( 0x63 ) ;
} else {
w - > writeC ( 0x61 ) ;
w - > writeS ( totalWait ) ;
}
tickCount + = totalWait ;
}
2023-02-08 19:25:03 -05:00
if ( writeLoop & & ! alreadyWroteLoop ) {
2022-02-18 12:58:36 -05:00
writeLoop = false ;
2023-02-08 19:25:03 -05:00
alreadyWroteLoop = true ;
2022-02-18 12:58:36 -05:00
loopPos = w - > tell ( ) ;
2023-02-08 19:25:03 -05:00
loopTickSong = songTick ;
2022-02-18 12:58:36 -05:00
}
}
// end of song
w - > writeC ( 0x66 ) ;
got . rate = origRate ;
for ( int i = 0 ; i < song . systemLen ; i + + ) {
disCont [ i ] . dispatch - > toggleRegisterDump ( false ) ;
}
// write GD3 tag
gd3Off = w - > tell ( ) ;
w - > write ( " Gd3 " , 4 ) ;
w - > writeI ( 0x100 ) ;
w - > writeI ( 0 ) ; // length. will be written later
WString ws ;
ws = utf8To16 ( song . name . c_str ( ) ) ;
w - > writeWString ( ws , false ) ; // name
2022-07-23 18:02:03 -04:00
ws = utf8To16 ( song . nameJ . c_str ( ) ) ;
w - > writeWString ( ws , false ) ; // japanese name
ws = utf8To16 ( song . category . c_str ( ) ) ;
w - > writeWString ( ws , false ) ; // game name
ws = utf8To16 ( song . categoryJ . c_str ( ) ) ;
w - > writeWString ( ws , false ) ; // japanese game name
ws = utf8To16 ( song . systemName . c_str ( ) ) ;
2022-02-18 12:58:36 -05:00
w - > writeWString ( ws , false ) ; // system name
2022-07-23 18:02:03 -04:00
ws = utf8To16 ( song . systemNameJ . c_str ( ) ) ;
2022-02-18 12:58:36 -05:00
w - > writeWString ( ws , false ) ; // japanese system name
ws = utf8To16 ( song . author . c_str ( ) ) ;
w - > writeWString ( ws , false ) ; // author name
2022-07-23 18:02:03 -04:00
ws = utf8To16 ( song . authorJ . c_str ( ) ) ;
w - > writeWString ( ws , false ) ; // japanese author name
2022-02-18 12:58:36 -05:00
w - > writeS ( 0 ) ; // date
2023-08-12 17:55:23 -04:00
w - > writeWString ( L " Furnace (chiptune tracker) " , false ) ; // ripper
2024-08-23 18:46:09 -04:00
ws = utf8To16 ( song . notes . c_str ( ) ) ;
w - > writeWString ( ws , false ) ; // notes
2022-02-18 12:58:36 -05:00
int gd3Len = w - > tell ( ) - gd3Off - 12 ;
w - > seek ( gd3Off + 8 , SEEK_SET ) ;
w - > writeI ( gd3Len ) ;
// finish file
size_t len = w - > size ( ) - 4 ;
w - > seek ( 4 , SEEK_SET ) ;
w - > writeI ( len ) ;
w - > seek ( 0x14 , SEEK_SET ) ;
w - > writeI ( gd3Off - 0x14 ) ;
w - > writeI ( tickCount ) ;
if ( loop ) {
2022-02-20 23:13:52 -05:00
if ( loopPos = = - 1 ) {
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
2023-02-08 19:25:03 -05:00
} else if ( loopTickSong < 0 | | loopTickSong > ( int ) tickPos . size ( ) ) {
logW ( " loopTickSong out of range! %d>%d " , loopTickSong , ( int ) tickPos . size ( ) ) ;
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
2022-02-20 23:13:52 -05:00
} else {
2023-02-08 19:25:03 -05:00
int realLoopTick = tickSample [ loopTickSong ] ;
int realLoopPos = tickPos [ loopTickSong ] ;
logI ( " tickCount-realLoopTick: %d. realLoopPos: %d " , tickCount - realLoopTick , realLoopPos ) ;
w - > writeI ( realLoopPos - 0x1c ) ;
w - > writeI ( tickCount - realLoopTick ) ;
2022-02-20 23:13:52 -05:00
}
2022-02-18 12:58:36 -05:00
} else {
w - > writeI ( 0 ) ;
w - > writeI ( 0 ) ;
}
2023-04-20 05:16:56 -04:00
if ( mayWriteRate ) {
w - > writeI ( round ( curSubSong - > hz ) ) ;
}
2022-02-18 12:58:36 -05:00
w - > seek ( 0x34 , SEEK_SET ) ;
w - > writeI ( songOff - 0x34 ) ;
2022-10-09 18:24:24 -04:00
if ( version > = 0x170 ) {
2022-02-18 12:58:36 -05:00
w - > seek ( 0xbc , SEEK_SET ) ;
w - > writeI ( exHeaderOff - 0xbc ) ;
2022-10-09 18:24:24 -04:00
}
2022-02-18 12:58:36 -05:00
remainingLoops = - 1 ;
playing = false ;
freelance = false ;
extValuePresent = false ;
2022-04-10 23:12:02 -04:00
logI ( " %d register writes total. " , writeCount ) ;
2022-02-18 12:58:36 -05:00
2025-07-26 19:48:23 -04:00
delete [ ] sampleOff8 ;
delete [ ] sampleLen8 ;
delete [ ] sampleOffSegaPCM ;
2022-03-23 22:38:28 -04:00
BUSY_END ;
2022-02-18 12:58:36 -05:00
return w ;
}