Beispiel #1
0
blargg_err_t Sap_Emu::start_track_( int track )
{
	RETURN_ERR( Classic_Emu::start_track_( track ) );
	
	core.setup_ram();
	
	// Copy file data to RAM
	byte const* in = info_.rom_data;
	while ( file_end - in >= 5 )
	{
		int start = get_le16( in );
		int end   = get_le16( in + 2 );
		//dprintf( "Block $%04X-$%04X\n", start, end );
		in += 4;
		int len = end - start + 1;
		if ( (unsigned) len > (unsigned) (file_end - in) )
		{
			set_warning( "Invalid file data block" );
			break;
		}
		
		memcpy( core.ram() + start, in, len );
		in += len;
		if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF )
			in += 2;
	}
	
	return core.start_track( track, info_ );
}
Beispiel #2
0
void Hes_Emu::cpu_write_vdp( int addr, int data )
{
	switch ( addr )
	{
	case 0:
		vdp.latch = data & 0x1F;
		break;
	
	case 2:
		if ( vdp.latch == 5 )
		{
			if ( data & 0x04 )
				set_warning( "Scanline interrupt unsupported" );
			run_until( time() );
			vdp.control = data;
			irq_changed();
		}
		else
		{
			debug_printf( "VDP not supported: $%02X <- $%02X\n", vdp.latch, data );
		}
		break;
	
	case 3:
		debug_printf( "VDP MSB not supported: $%02X <- $%02X\n", vdp.latch, data );
		break;
	}
}
Beispiel #3
0
blargg_err_t Hes_Emu::run_clocks( blip_time_t& duration_, int )
{
	blip_time_t const duration = duration_; // cache
	
	if ( cpu::run( duration ) )
		set_warning( "Emulation error (illegal instruction)" );
	
	check( time() >= duration );
	//check( time() - duration < 20 ); // Txx instruction could cause going way over
	
	run_until( duration );
	
	// end time frame
	timer.last_time -= duration;
	vdp.next_vbl    -= duration;
	#if GME_FRAME_HOOK_DEFINED
		last_frame_hook -= duration;
	#endif
	cpu::end_frame( duration );
	::adjust_time( irq.timer, duration );
	::adjust_time( irq.vdp,   duration );
	apu.end_frame( duration );
	
	return 0;
}
Beispiel #4
0
blargg_err_t Ay_Emu::load_mem_( byte const in [], int size )
{
    assert( offsetof (header_t,track_info [2]) == header_t::size );

    RETURN_ERR( parse_header( in, size, &file ) );
    set_track_count( file.header->max_track + 1 );

    if ( file.header->vers > 2 )
        set_warning( "Unknown file version" );

    int const osc_count = Ay_Apu::osc_count + 1; // +1 for beeper

    set_voice_count( osc_count );
    core.apu().volume( gain() );

    static const char* const names [osc_count] = {
        "Wave 1", "Wave 2", "Wave 3", "Beeper"
    };
    set_voice_names( names );

    static int const types [osc_count] = {
        wave_type+0, wave_type+1, wave_type+2, mixed_type+1
    };
    set_voice_types( types );

    return setup_buffer( spectrum_clock );
}
Beispiel #5
0
blargg_err_t Sap_Emu::load_mem_( byte const in [], int size )
{
	file_end = in + size;
	
	info_.warning    = NULL;
	info_.type       = 'B';
	info_.stereo     = false;
	info_.init_addr  = -1;
	info_.play_addr  = -1;
	info_.music_addr = -1;
	info_.fastplay   = 312;
	RETURN_ERR( parse_info( in, size, &info_ ) );
	
	set_warning( info_.warning );
	set_track_count( info_.track_count );
	set_voice_count( Sap_Apu::osc_count << info_.stereo );
	core.apu_impl().volume( gain() );
	
	static const char* const names [Sap_Apu::osc_count * 2] = {
		"Wave 1", "Wave 2", "Wave 3", "Wave 4",
		"Wave 5", "Wave 6", "Wave 7", "Wave 8",
	};
	set_voice_names( names );
	
	static int const types [Sap_Apu::osc_count * 2] = {
		wave_type+1, wave_type+2, wave_type+3, wave_type+0,
		wave_type+5, wave_type+6, wave_type+7, wave_type+4,
	};
	set_voice_types( types );
	
	return setup_buffer( 1773447 );
}
Beispiel #6
0
blargg_err_t Gme_File::load_m3u_( blargg_err_t err )
{
	if ( !err )
	{
		require( raw_track_count_ ); // file must be loaded first
		if ( playlist.size() )
			track_count_ = playlist.size();
		
		int line = playlist.first_error();
		if ( line )
		{
			// avoid using bloated printf()
			char* out = &playlist_warning [sizeof playlist_warning];
			*--out = 0;
			do {
				*--out = line % 10 + '0';
			} while ( (line /= 10) > 0 );
			
			static const char str [] = "Problem in m3u at line ";
			out -= sizeof str - 1;
			memcpy( out, str, sizeof str - 1 );
			set_warning( out );
		}
	}
	return err;
}
Beispiel #7
0
blargg_err_t Sap_Emu::start_track_( int track )
{
	RETURN_ERR( Classic_Emu::start_track_( track ) );
	
	memset( &mem, 0, sizeof mem );

	byte const* in = info.rom_data;
	while ( file_end - in >= 5 )
	{
		unsigned start = get_le16( in );
		unsigned end   = get_le16( in + 2 );
		//debug_printf( "Block $%04X-$%04X\n", start, end );
		in += 4;
		if ( end < start )
		{
			set_warning( "Invalid file data block" );
			break;
		}
		long len = end - start + 1;
		if ( len > file_end - in )
		{
			set_warning( "Invalid file data block" );
			break;
		}
		
		memcpy( mem.ram + start, in, len );
		in += len;
		if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF )
			in += 2;
	}
	
	apu.reset( &apu_impl );
	apu2.reset( &apu_impl );
	cpu::reset( mem.ram );
	time_mask = 0; // disables sound during init
	call_init( track );
	time_mask = -1;
	
	next_play = play_period();
	
	return 0;
}
Beispiel #8
0
	blargg_err_t load_( Data_Reader& in )
	{
		blargg_err_t err = in.read( &h, Nsf_Emu::header_size );
		if ( err )
			return (err == in.eof_error ? gme_wrong_file_type : err);
		
		if ( h.chip_flags & ~(namco_flag | vrc6_flag | fme7_flag) )
			set_warning( "Uses unsupported audio expansion hardware" );
		
		set_track_count( h.track_count );
		return check_nsf_header( &h );
	}
Beispiel #9
0
blargg_err_t Hes_Emu::load_( Data_Reader& in )
{
	assert( offsetof (header_t,unused [4]) == header_size );
	RETURN_ERR( rom.load( in, header_size, &header_, unmapped ) );
	
	RETURN_ERR( check_hes_header( header_.tag ) );
	
	if ( header_.vers != 0 )
		set_warning( "Unknown file version" );
	
	if ( memcmp( header_.data_tag, "DATA", 4 ) )
		set_warning( "Data header missing" );
	
	if ( memcmp( header_.unused, "\0\0\0\0", 4 ) )
		set_warning( "Unknown header data" );
	
	// File spec supports multiple blocks, but I haven't found any, and
	// many files have bad sizes in the only block, so it's simpler to
	// just try to load the damn data as best as possible.
	
	long addr = get_le32( header_.addr );
	long size = get_le32( header_.size );
	long const rom_max = 0x100000;
	if ( addr & ~(rom_max - 1) )
	{
		set_warning( "Invalid address" );
		addr &= rom_max - 1;
	}
	if ( (unsigned long) (addr + size) > (unsigned long) rom_max )
		set_warning( "Invalid size" );
	
	if ( size != rom.file_size() )
	{
		if ( size <= rom.file_size() - 4 && !memcmp( rom.begin() + size, "DATA", 4 ) )
			set_warning( "Multiple DATA not supported" );
		else if ( size < rom.file_size() )
			set_warning( "Extra file data" );
		else
			set_warning( "Missing file data" );
	}
	
	rom.set_addr( addr );
	
	set_voice_count( apu.osc_count );
	
	apu.volume( gain() );
	
	return setup_buffer( 7159091 );
}
Beispiel #10
0
blargg_err_t Nsf_Impl::load_( Data_Reader& in )
{
	// pad ROM data with 0
	RETURN_ERR( rom.load( in, header_.size, &header_, 0 ) );
	
	if ( !header_.valid_tag() )
		return blargg_err_file_type;
	
	RETURN_ERR( high_ram.resize( (fds_enabled() ? fdsram_offset + fdsram_size : fdsram_offset) ) );

	addr_t load_addr = get_addr( header_.load_addr );
	if ( load_addr < (fds_enabled() ? sram_addr : rom_addr) )
		set_warning( "Load address is too low" );
	
	rom.set_addr( load_addr % bank_size );
	
	if ( header_.vers != 1 )
		set_warning( "Unknown file version" );
	
	set_play_period( header_.play_period() );
	
	return blargg_ok;
}
Beispiel #11
0
blargg_err_t Sgc_Core::end_frame( time_t t )
{
	RETURN_ERR( Sgc_Impl::end_frame( t ) );
	apu_.end_frame( t );
	if ( sega_mapping() && fm_accessed )
	{
		if ( fm_apu_.supported() )
			fm_apu_.end_frame( t );
		else
			set_warning( "FM sound not supported" );
	}

	return blargg_ok;
}
Beispiel #12
0
blargg_err_t Sgc_Impl::load_( Data_Reader& in )
{
	RETURN_ERR( rom.load( in, header_.size, &header_, 0 ) );
	
	if ( !header_.valid_tag() )
		return blargg_err_file_type;
	
	if ( header_.vers != 1 )
		set_warning( "Unknown file version" );
	
	if ( header_.system > 2 )
		set_warning( "Unknown system" );
	
	addr_t load_addr = get_le16( header_.load_addr );
	if ( load_addr < 0x400 )
		set_warning( "Invalid load address" );
	
	rom.set_addr( load_addr );
	play_period = clock_rate() / 60;
	
	if ( sega_mapping() )
	{
		RETURN_ERR( ram.resize( 0x2000 + Sgc_Cpu::page_padding ) );
		RETURN_ERR( ram2.resize( bank_size + Sgc_Cpu::page_padding ) );
	}
	else
	{
		RETURN_ERR( ram.resize( 0x400 + Sgc_Cpu::page_padding ) );
	}
	
	RETURN_ERR( vectors.resize( Sgc_Cpu::page_size + Sgc_Cpu::page_padding ) );
	
	// TODO: doesn't need to be larger than page size, if we do mapping calls right
	RETURN_ERR( unmapped_write.resize( bank_size ) );
	
	return blargg_ok;
}
Beispiel #13
0
blargg_err_t Ay_Emu::load_mem_( byte const* in, long size )
{
	assert( offsetof (header_t,track_info [2]) == header_size );
	
	RETURN_ERR( parse_header( in, size, &file ) );
	set_track_count( file.header->max_track + 1 );
	
	if ( file.header->vers > 2 )
		set_warning( "Unknown file version" );
	
	set_voice_count( osc_count );
	apu.volume( gain() );
	
	return setup_buffer( spectrum_clock );
}
Beispiel #14
0
blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int )
{
	set_time( 0 );
	while ( time() < duration )
	{
		nes_time_t end = min( next_play, duration );
		end = min( end, time() + 32767 ); // allows CPU to use 16-bit time delta
		if ( cpu::run( end ) )
		{
			if ( r.pc != badop_addr )
			{
				set_warning( "Emulation error (illegal instruction)" );
				r.pc++;
			}
			else
			{
				play_ready = 1;
				if ( saved_state.pc != badop_addr )
				{
					cpu::r = saved_state;
					saved_state.pc = badop_addr;
				}
				else
				{
					set_time( end );
				}
			}
		}
		
		if ( time() >= next_play )
		{
			nes_time_t period = (play_period + play_extra) / clock_divisor;
			play_extra = play_period - period * clock_divisor;
			next_play += period;
			if ( play_ready && !--play_ready )
			{
				check( saved_state.pc == badop_addr );
				if ( r.pc != badop_addr )
					saved_state = cpu::r;
				
				r.pc = play_addr;
				low_mem [0x100 + r.sp--] = (badop_addr - 1) >> 8;
				low_mem [0x100 + r.sp--] = (badop_addr - 1) & 0xFF;
				GME_FRAME_HOOK( this );
			}
		}
Beispiel #15
0
blargg_err_t Sap_Emu::load_mem_( byte const* in, long size )
{
	file_end = in + size;
	
	info.warning    = 0;
	info.type       = 'B';
	info.stereo     = false;
	info.init_addr  = -1;
	info.play_addr  = -1;
	info.music_addr = -1;
	info.fastplay   = 312;
	RETURN_ERR( parse_info( in, size, &info ) );
	
	set_warning( info.warning );
	set_track_count( info.track_count );
	set_voice_count( Sap_Apu::osc_count << info.stereo );
	apu_impl.volume( gain() );
	
	return setup_buffer( 1773447 );
}
Beispiel #16
0
blargg_err_t Gbs_Emu::load_( Data_Reader& in )
{
	RETURN_ERR( core_.load( in ) );
	set_warning( core_.warning() );
	set_track_count( header().track_count );
	set_voice_count( Gb_Apu::osc_count );
	core_.apu().volume( gain() );
	
	static const char* const names [Gb_Apu::osc_count] = {
		"Square 1", "Square 2", "Wave", "Noise"
	};
	set_voice_names( names );
	
	static int const types [Gb_Apu::osc_count] = {
		wave_type+1, wave_type+2, wave_type+3, mixed_type+1
	};
	set_voice_types( types );
	
	return setup_buffer( 4194304 );
}
Beispiel #17
0
blargg_err_t Nsf_Impl::start_track( int track )
{
	int speed_flags = 0;
	#if NSF_EMU_EXTRA_FLAGS
		speed_flags = header().speed_flags;
	#endif
	
	apu.reset( header().pal_only(), (speed_flags & 0x20) ? 0x3F : 0 );
	apu.enable_w4011_( enable_w4011 );
	apu.write_register( 0, 0x4015, 0x0F );
	apu.write_register( 0, 0x4017, (speed_flags & 0x10) ? 0x80 : 0 );
	
	// Clear memory
	memset( unmapped_code(), Nes_Cpu::halt_opcode, unmapped_size );
	memset( low_ram, 0, low_ram_size );
	memset( sram(), 0, sram_size );
	
	map_memory();
	
	// Arrange time of first call to play routine
	play_extra = 0;
	next_play  = play_period;
	
	play_delay = initial_play_delay;
	saved_state.pc = idle_addr;
	
	// Setup for call to init routine
	cpu.r.a  = track;
	cpu.r.x  = header_.pal_only();
	cpu.r.sp = 0xFF;
	jsr_then_stop( header_.init_addr );
	if ( cpu.r.pc < get_addr( header_.load_addr ) )
		set_warning( "Init address < load address" );
	
	return blargg_ok;
}
Beispiel #18
0
blargg_err_t Kss_Emu::load_( Data_Reader& in )
{
	RETURN_ERR( core.load( in ) );
	set_warning( core.warning() );

	set_track_count( get_le16( header().last_track ) + 1 );

	core.scc_enabled = false;
	if ( header().device_flags & 0x02 ) // Sega Master System
	{
		int const osc_count = Sms_Apu::osc_count + Opl_Apu::osc_count;
		static const char* const names [osc_count] = {
			"Square 1", "Square 2", "Square 3", "Noise", "FM"
		};
		set_voice_names( names );

		static int const types [osc_count] = {
			wave_type+1, wave_type+3, wave_type+2, mixed_type+1, wave_type+0
		};
		set_voice_types( types );

		// sms.psg
		set_voice_count( Sms_Apu::osc_count );
		check( !core.sms.psg );
		CHECK_ALLOC( core.sms.psg = BLARGG_NEW Sms_Apu );

		// sms.fm
		if ( header().device_flags & 0x01 )
		{
			set_voice_count( osc_count );
			RETURN_ERR( new_opl_apu( Opl_Apu::type_smsfmunit, &core.sms.fm ) );
		}

	}
	else // MSX
	{
		int const osc_count = Ay_Apu::osc_count + Opl_Apu::osc_count;
		static const char* const names [osc_count] = {
			"Square 1", "Square 2", "Square 3", "FM"
		};
		set_voice_names( names );

		static int const types [osc_count] = {
			wave_type+1, wave_type+3, wave_type+2, wave_type+0
		};
		set_voice_types( types );

		// msx.psg
		set_voice_count( Ay_Apu::osc_count );
		check( !core.msx.psg );
		CHECK_ALLOC( core.msx.psg = BLARGG_NEW Ay_Apu );

		if ( header().device_flags & 0x10 )
			set_warning( "MSX stereo not supported" );

		// msx.music
		if ( header().device_flags & 0x01 )
		{
			set_voice_count( osc_count );
			RETURN_ERR( new_opl_apu( Opl_Apu::type_msxmusic, &core.msx.music ) );
		}

		// msx.audio
		if ( header().device_flags & 0x08 )
		{
			set_voice_count( osc_count );
			RETURN_ERR( new_opl_apu( Opl_Apu::type_msxaudio, &core.msx.audio ) );
		}

		if ( !(header().device_flags & 0x80) )
		{
			if ( !(header().device_flags & 0x84) )
				core.scc_enabled = core.scc_enabled_true;

			// msx.scc
			check( !core.msx.scc );
			CHECK_ALLOC( core.msx.scc = BLARGG_NEW Scc_Apu );

			int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count;
			static const char* const names [osc_count] = {
				"Square 1", "Square 2", "Square 3",
				"Wave 1", "Wave 2", "Wave 3", "Wave 4", "Wave 5"
			};
			set_voice_names( names );

			static int const types [osc_count] = {
				wave_type+1, wave_type+3, wave_type+2,
				wave_type+0, wave_type+4, wave_type+5, wave_type+6, wave_type+7,
			};
			set_voice_types( types );

			set_voice_count( osc_count );
		}
	}

	set_silence_lookahead( 6 );
	if ( core.sms.fm || core.msx.music || core.msx.audio )
	{
		if ( !Opl_Apu::supported() )
			set_warning( "FM sound not supported" );
		else
			set_silence_lookahead( 3 ); // Opl_Apu is really slow
	}

	return setup_buffer( ::clock_rate );
}
Beispiel #19
0
configuration::configuration(const char* file_name)
{
    _warning = false;
    _file_name = std::string(file_name);

    FILE* fd = ::fopen(file_name, "rb");
    if (fd == nullptr) 
    {
        printf("Cannot open file %s, err=%s", file_name, strerror(errno));
        return;
    }
    ::fseek(fd, 0, SEEK_END);
    int len = ftell(fd);
    if (len == -1 || len == 0) 
    {
        printf("Cannot get length of %s, err=%s", file_name, strerror(errno));
        ::fclose(fd);
        return;
    }

    int fileLength = len;
    _file_data.reset((char*)malloc(len+1));
    char* fileData = _file_data.get();

    ::fseek(fd, 0, SEEK_SET);
    auto sz = ::fread(fileData, len, 1, fd);
    ::fclose(fd);
    if (sz != 1)
    {
        printf("Cannot read correct data of %s, err=%s", file_name, strerror(errno));
        return;
    }
    ((char*)fileData)[fileLength] = '\n';

    //
    // parse mapped file and build conf map
    //
    std::map<std::string, conf>* pSection = nullptr;
    char *p, *pLine = (char*)"", *pNextLine, *pEnd, *pSectionName = nullptr, *pEqual;
    int lineno = 0;
    unsigned int indexInSection = 0;

    p = (char*)fileData;
    pEnd = p + fileLength;

    while (p < pEnd) {
        //
        // get line
        //
        lineno++;
        while (*p == ' ' || *p == '\t' || *p == '\r')    p++;

        pLine = p;
        int shift = 0;
        while (*p != '\n' && p < pEnd)    
        {
            if (*p == '#' || *p == ';')
            {
                if (p != pLine && *(p-1) == '^')
                {
                    shift++;
                }
                else
                {
                    *p = '\0';
                }
            }

            if (shift > 0)
            {
                *(p-shift) = *p;
            }
            p++;
        }
        *(p-shift) = '\0';
        pNextLine = ++p;

        //
        // parse line
        //
        p = pLine;
        if (*p == '\0')    goto Next;    // skip comment line or empty line
        pEqual = strchr(p, '=');
        if (nullptr == pEqual && *p != '[') {
            goto ConfReg;
        }
        if (nullptr != pEqual && *p == '[') 
            goto err;

        //
        //    conf
        //
        if (pEqual) 
        {
ConfReg:
            if (pSection == nullptr) {
                printf("configuration section not defined");
                goto err;
            }
            if (pEqual)    *pEqual = '\0';
            char* pKey = utils::trim_string(p);
            char* pValue = pEqual ? utils::trim_string(++pEqual) : nullptr;
            if (*pKey == '\0')    
                goto err;

            if (pSection->find((const char*)pKey) != pSection->end()) 
            {
                auto it = pSection->find((const char*)pKey);

                printf("Warning: skip redefinition of option [%s] %s (line %u), already defined as [%s] %s (line %u)\n", 
                    pSectionName,
                    pKey,
                    lineno,
                    it->second.section,
                    it->second.key,
                    it->second.line
                    );
            }
            else
            {
                conf cf;
                cf.section = (const char*)pSectionName;
                cf.key = (const char*)pKey;
                cf.value = pValue;
                cf.line = lineno; 
                pSection->insert(std::make_pair(std::string(pKey), cf));
            }            
        }
        //
        //    section
        //
        else 
        {
            char* pRight = strchr(p, ']');
            if (nullptr == pRight)   
                goto err;
            *pRight = '\0';
            p++;
            pSectionName = utils::trim_string(p);
            if (*pSectionName == '\0')   
                goto err;

            bool old = set_warning(false);
            if (has_section((const char*)pSectionName)) {
                printf("RedefInition of section %s\n", pSectionName);
                set_warning(old);
                goto err;
            }
            set_warning(old);

            std::map<std::string, conf> sm;
            auto it = _configs.insert(config_map::value_type(std::string(pSectionName), sm));
            assert (it.second);
            pSection = &it.first->second;
            indexInSection = 0;
        }

        //
        // iterate nextline
        //
Next:
        p = pNextLine;
    }
    return;
    
err:
    printf("Unexpected configure in %s(line %d): %s\n", file_name, lineno, pLine);
    exit(-2);
}
int main (int argc, char **argv) {
    /* Local Vars */
    int ret;
    int i;
    unsigned int len;
    gnutls_x509_crt_t cert[1];
    char *subject = NULL;
    size_t subject_len;
    time_t expiration_time, activation_time;


    /* Set signal handling and alarm */
    if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR)
        critical("Setup SIGALRM trap failed!");

    /* Process check arguments */
    if (process_arguments(argc, argv) != OK)
        unknown("Parsing arguments failed!");

    /* Start plugin timeout */
    alarm(mp_timeout);

    /* Init GnuTLS */
    gnutls_global_init();

    for(i = 0; i < cert_files; i++) {
        if (mp_verbose)
            printf("Cert: %s\n", cert_file[i]);

        /* Read the Cert */
        gnutls_datum_t data = { NULL, 0 };
        ret = mp_slurp(cert_file[i], &(data.data));
        data.size = ret;
        if (ret <= 0) {
            set_critical("Error loading cert file '%s'.", cert_file[i]);
            continue;
        }

        /* Load the Cert to a list. */
        len = 1;
        ret = gnutls_x509_crt_list_import(cert, &len, &data,
                                          GNUTLS_X509_FMT_PEM,
                                          GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
        if (ret < 0) {
            set_critical("%s error: %s", cert_file[i], gnutls_strerror(ret));
            continue;
        };

        /* Read der Cert CN */
        if (subject == NULL) {
            subject = mp_malloc(128);
            subject_len = 128;
        }
        ret = gnutls_x509_crt_get_dn_by_oid(cert[0],
                                            GNUTLS_OID_X520_COMMON_NAME, 0, 0,
                                            subject, &subject_len);
        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
            subject_len+=1;
            subject = mp_realloc(subject, subject_len);
            ret = gnutls_x509_crt_get_dn_by_oid(cert[0],
                                                GNUTLS_OID_X520_COMMON_NAME, 0, 0,
                                                subject, &subject_len);
        }
        if (ret != 0) {
            set_critical("%s error: %s", cert_file[i], gnutls_strerror(ret));
            continue;
        }
        if (mp_verbose) {
            printf(" * Subject: %s\n", subject);
        }

        /* Check expire time */
        expiration_time = gnutls_x509_crt_get_expiration_time (cert[0]);
        activation_time = gnutls_x509_crt_get_activation_time (cert[0]);

        if (mp_verbose) {
            printf (" * Certificate is valid since: %s", ctime (&activation_time));
            printf (" * Certificate expires: %s", ctime (&expiration_time));
        }

        int days = (int)difftime(expiration_time, time(0))/86400;
        switch (get_status((expiration_time-time(0)), expire_thresholds)) {
        case STATE_OK:
            set_ok(cert_file[i]);
            break;
        case STATE_WARNING:
            set_warning("%s expires in %d day%s", cert_file[i], days, days==1?"":"s");
            break;
        case STATE_CRITICAL:
            set_critical("%s expires in %d day%s", cert_file[i], days, days==1?"":"s");
            break;
        }

        if (activation_time > time(0)) {
            int days = (int)difftime(activation_time, time(0))/86400;
            set_critical("%s activates in %d day%s", cert_file[i], days, days==1?"":"s");
        }
    }

    // Dissconnect
    gnutls_global_deinit ();

    mp_exit("X509");
}
Beispiel #21
0
blargg_err_t Nsf_Emu::load_( Data_Reader& in )
{
	assert( offsetof (header_t,unused [4]) == header_size );
	RETURN_ERR( rom.load( in, header_size, &header_, 0 ) );
	
	set_track_count( header_.track_count );
	RETURN_ERR( check_nsf_header( &header_ ) );
	
	if ( header_.vers != 1 )
		set_warning( "Unknown file version" );
	
	// sound and memory
	blargg_err_t err = init_sound();
	if ( err )
		return err;
	
	// set up data
	nes_addr_t load_addr = get_le16( header_.load_addr );
	init_addr = get_le16( header_.init_addr );
	play_addr = get_le16( header_.play_addr );
	if ( !load_addr ) load_addr = rom_begin;
	if ( !init_addr ) init_addr = rom_begin;
	if ( !play_addr ) play_addr = rom_begin;
	if ( load_addr < rom_begin || init_addr < rom_begin )
	{
		const char* w = warning();
		if ( !w )
			w = "Corrupt file (invalid load/init/play address)";
		return w;
	}
	
	rom.set_addr( load_addr % bank_size );
	int total_banks = rom.size() / bank_size;
	
	// bank switching
	int first_bank = (load_addr - rom_begin) / bank_size;
	for ( int i = 0; i < bank_count; i++ )
	{
		unsigned bank = i - first_bank;
		if ( bank >= (unsigned) total_banks )
			bank = 0;
		initial_banks [i] = bank;
		
		if ( header_.banks [i] )
		{
			// bank-switched
			memcpy( initial_banks, header_.banks, sizeof initial_banks );
			break;
		}
	}
	
	pal_only = (header_.speed_flags & 3) == 1;
	
	#if !NSF_EMU_EXTRA_FLAGS
		header_.speed_flags = 0;
	#endif
	
	set_tempo( tempo() );
	
	return setup_buffer( (long) (clock_rate_ + 0.5) );
}
Beispiel #22
0
blargg_err_t Nsf_Emu::init_sound()
{
	if ( header_.chip_flags & ~(namco_flag | vrc6_flag | fme7_flag) )
		set_warning( "Uses unsupported audio expansion hardware" );
	
	{
		#define APU_NAMES "Square 1", "Square 2", "Triangle", "Noise", "DMC"
		
		int const count = Nes_Apu::osc_count;
		static const char* const apu_names [count] = { APU_NAMES };
		set_voice_count( count );
		set_voice_names( apu_names );
		
	}
	
	static int const types [] = {
		wave_type  | 1, wave_type  | 2, wave_type | 0,
		noise_type | 0, mixed_type | 1,
		wave_type  | 3, wave_type  | 4, wave_type | 5,
		wave_type  | 6, wave_type  | 7, wave_type | 8, wave_type | 9,
		wave_type  |10, wave_type  |11, wave_type |12, wave_type |13
	};
	set_voice_types( types ); // common to all sound chip configurations
	
	double adjusted_gain = gain();
	
	#if NSF_EMU_APU_ONLY
	{
		if ( header_.chip_flags )
			set_warning( "Uses unsupported audio expansion hardware" );
	}
	#else
	{
		if ( header_.chip_flags & (namco_flag | vrc6_flag | fme7_flag) )
			set_voice_count( Nes_Apu::osc_count + 3 );
		
		if ( header_.chip_flags & namco_flag )
		{
			namco = BLARGG_NEW Nes_Namco_Apu;
			CHECK_ALLOC( namco );
			adjusted_gain *= 0.75;
			
			int const count = Nes_Apu::osc_count + Nes_Namco_Apu::osc_count;
			static const char* const names [count] = {
				APU_NAMES,
				"Wave 1", "Wave 2", "Wave 3", "Wave 4",
				"Wave 5", "Wave 6", "Wave 7", "Wave 8"
			};
			set_voice_count( count );
			set_voice_names( names );
		}
		
		if ( header_.chip_flags & vrc6_flag )
		{
			vrc6 = BLARGG_NEW Nes_Vrc6_Apu;
			CHECK_ALLOC( vrc6 );
			adjusted_gain *= 0.75;
			
			{
				int const count = Nes_Apu::osc_count + Nes_Vrc6_Apu::osc_count;
				static const char* const names [count] = {
					APU_NAMES,
					"Saw Wave", "Square 3", "Square 4"
				};
				set_voice_count( count );
				set_voice_names( names );
			}
			
			if ( header_.chip_flags & namco_flag )
			{
				int const count = Nes_Apu::osc_count + Nes_Vrc6_Apu::osc_count +
						Nes_Namco_Apu::osc_count;
				static const char* const names [count] = {
					APU_NAMES,
					"Saw Wave", "Square 3", "Square 4",
					"Wave 1", "Wave 2", "Wave 3", "Wave 4",
					"Wave 5", "Wave 6", "Wave 7", "Wave 8"
				};
				set_voice_count( count );
				set_voice_names( names );
			}
		}
		
		if ( header_.chip_flags & fme7_flag )
		{
			fme7 = BLARGG_NEW Nes_Fme7_Apu;
			CHECK_ALLOC( fme7 );
			adjusted_gain *= 0.75;
			
			int const count = Nes_Apu::osc_count + Nes_Fme7_Apu::osc_count;
			static const char* const names [count] = {
				APU_NAMES,
				"Square 3", "Square 4", "Square 5"
			};
			set_voice_count( count );
			set_voice_names( names );
		}
		
		if ( namco ) namco->volume( adjusted_gain );
		if ( vrc6  ) vrc6 ->volume( adjusted_gain );
		if ( fme7  ) fme7 ->volume( adjusted_gain );
	}
	#endif
	
	apu.volume( adjusted_gain );
	
	return 0;
}
Beispiel #23
0
blargg_err_t Ay_Emu::start_track_( int track )
{
    RETURN_ERR( Classic_Emu::start_track_( track ) );

    byte* const mem = core.mem();

    memset( mem + 0x0000, 0xC9, 0x100 ); // fill RST vectors with RET
    memset( mem + 0x0100, 0xFF, 0x4000 - 0x100 );
    memset( mem + core.ram_addr, 0x00, core.mem_size - core.ram_addr );

    // locate data blocks
    byte const* const data = get_data( file, file.tracks + track * 4 + 2, 14 );
    if ( !data )
        return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "file data missing" );

    byte const* const more_data = get_data( file, data + 10, 6 );
    if ( !more_data )
        return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "file data missing" );

    byte const* blocks = get_data( file, data + 12, 8 );
    if ( !blocks )
        return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "file data missing" );

    // initial addresses
    unsigned addr = get_be16( blocks );
    if ( !addr )
        return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "file data missing" );

    unsigned init = get_be16( more_data + 2 );
    if ( !init )
        init = addr;

    // copy blocks into memory
    do
    {
        blocks += 2;
        unsigned len = get_be16( blocks );
        blocks += 2;
        if ( addr + len > core.mem_size )
        {
            set_warning( "Bad data block size" );
            len = core.mem_size - addr;
        }
        check( len );
        byte const* in = get_data( file, blocks, 0 );
        blocks += 2;
        if ( len > (unsigned) (file.end - in) )
        {
            set_warning( "File data missing" );
            len = file.end - in;
        }
        //dprintf( "addr: $%04X, len: $%04X\n", addr, len );
        if ( addr < core.ram_addr && addr >= 0x400 ) // several tracks use low data
            dprintf( "Block addr in ROM\n" );
        memcpy( mem + addr, in, len );

        if ( file.end - blocks < 8 )
        {
            set_warning( "File data missing" );
            break;
        }
    }
    while ( (addr = get_be16( blocks )) != 0 );

    // copy and configure driver
    static byte const passive [] = {
        0xF3,       // DI
        0xCD, 0, 0, // CALL init
        0xED, 0x5E, // LOOP: IM 2
        0xFB,       // EI
        0x76,       // HALT
        0x18, 0xFA  // JR LOOP
    };
    static byte const active [] = {
        0xF3,       // DI
        0xCD, 0, 0, // CALL init
        0xED, 0x56, // LOOP: IM 1
        0xFB,       // EI
        0x76,       // HALT
        0xCD, 0, 0, // CALL play
        0x18, 0xF7  // JR LOOP
    };
    memcpy( mem, passive, sizeof passive );
    int const play_addr = get_be16( more_data + 4 );
    if ( play_addr )
    {
        memcpy( mem, active, sizeof active );
        mem [ 9] = play_addr;
        mem [10] = play_addr >> 8;
    }
    mem [2] = init;
    mem [3] = init >> 8;

    mem [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET)

    // start at spectrum speed
    change_clock_rate( spectrum_clock );
    set_tempo( tempo() );

    Ay_Core::registers_t r = { };
    r.sp = get_be16( more_data );
    r.b.a     = r.b.b = r.b.d = r.b.h = data [8];
    r.b.flags = r.b.c = r.b.e = r.b.l = data [9];
    r.alt.w = r.w;
    r.ix = r.iy = r.w.hl;

    core.start_track( r, play_addr );

    return blargg_ok;
}
Beispiel #24
0
blargg_err_t Ay_Emu::start_track_( int track )
{
	RETURN_ERR( Classic_Emu::start_track_( track ) );
	
	memset( mem.ram + 0x0000, 0xC9, 0x100 ); // fill RST vectors with RET
	memset( mem.ram + 0x0100, 0xFF, 0x4000 - 0x100 );
	memset( mem.ram + ram_start, 0x00, sizeof mem.ram - ram_start );
	memset( mem.padding1, 0xFF, sizeof mem.padding1 );
	memset( mem.ram + 0x10000, 0xFF, sizeof mem.ram - 0x10000 );
	
	// locate data blocks
	byte const* const data = get_data( file, file.tracks + track * 4 + 2, 14 );
	if ( !data ) return "File data missing";
	
	byte const* const more_data = get_data( file, data + 10, 6 );
	if ( !more_data ) return "File data missing";
	
	byte const* blocks = get_data( file, data + 12, 8 );
	if ( !blocks ) return "File data missing";
	
	// initial addresses
	cpu::reset( mem.ram );
	r.sp = get_be16( more_data );
	r.b.a = r.b.b = r.b.d = r.b.h = data [8];
	r.b.flags = r.b.c = r.b.e = r.b.l = data [9];
	r.alt.w = r.w;
	r.ix = r.iy = r.w.hl;
	
	unsigned addr = get_be16( blocks );
	if ( !addr ) return "File data missing";
	
	unsigned init = get_be16( more_data + 2 );
	if ( !init )
		init = addr;
	
	// copy blocks into memory
	do
	{
		blocks += 2;
		unsigned len = get_be16( blocks ); blocks += 2;
		if ( addr + len > 0x10000 )
		{
			set_warning( "Bad data block size" );
			len = 0x10000 - addr;
		}
		check( len );
		byte const* in = get_data( file, blocks, 0 ); blocks += 2;
		if ( len > blargg_ulong (file.end - in) )
		{
			set_warning( "Missing file data" );
			len = file.end - in;
		}
		//debug_printf( "addr: $%04X, len: $%04X\n", addr, len );
		if ( addr < ram_start && addr >= 0x400 ) // several tracks use low data
			debug_printf( "Block addr in ROM\n" );
		memcpy( mem.ram + addr, in, len );
		
		if ( file.end - blocks < 8 )
		{
			set_warning( "Missing file data" );
			break;
		}
	}
	while ( (addr = get_be16( blocks )) != 0 );
	
	// copy and configure driver
	static byte const passive [] = {
		0xF3,       // DI
		0xCD, 0, 0, // CALL init
		0xED, 0x5E, // LOOP: IM 2
		0xFB,       // EI
		0x76,       // HALT
		0x18, 0xFA  // JR LOOP
	};
	static byte const active [] = {
		0xF3,       // DI
		0xCD, 0, 0, // CALL init
		0xED, 0x56, // LOOP: IM 1
		0xFB,       // EI
		0x76,       // HALT
		0xCD, 0, 0, // CALL play
		0x18, 0xF7  // JR LOOP
	};
	memcpy( mem.ram, passive, sizeof passive );
	unsigned play_addr = get_be16( more_data + 4 );
	//debug_printf( "Play: $%04X\n", play_addr );
	if ( play_addr )
	{
		memcpy( mem.ram, active, sizeof active );
		mem.ram [ 9] = play_addr;
		mem.ram [10] = play_addr >> 8;
	}
	mem.ram [2] = init;
	mem.ram [3] = init >> 8;
	
	mem.ram [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET)
	
	memcpy( mem.ram + 0x10000, mem.ram, 0x80 ); // some code wraps around (ugh)
	
	beeper_delta = int (apu.amp_range * 0.65);
	last_beeper = 0;
	apu.reset();
	next_play = play_period;
	
	// start at spectrum speed
	change_clock_rate( spectrum_clock );
	set_tempo( tempo() );
	
	spectrum_mode = false;
	cpc_mode      = false;
	cpc_latch     = 0;
	
	return 0;
}
Beispiel #25
0
inline void Vgm_Emu::check_warning()
{
	const char* w = core.warning();
	if ( w )
		set_warning( w );
}
Beispiel #26
0
// arguments: k1=v1;k2=v2;k3=v3; ...
// e.g.,
//    port = %port%
//    timeout = %timeout%
// arguments: port=23466;timeout=1000 or arguments: ports=23466,timout=1000
bool configuration::load(const char* file_name, const char* arguments)
{
    _file_name = std::string(file_name);

    FILE* fd = ::fopen(file_name, "rb");
    if (fd == nullptr) 
    {
        std::string cdir;
        dsn::utils::filesystem::get_current_directory(cdir);
        printf("ERROR: cannot open file %s in %s, err = %s\n", file_name, cdir.c_str(), strerror(errno));
        return false;
    }
    ::fseek(fd, 0, SEEK_END);
    int len = ftell(fd);
    if (len == -1 || len == 0) 
    {
        printf("ERROR: cannot get length of %s, err = %s\n", file_name, strerror(errno));
        ::fclose(fd);
        return false;
    }

    _file_data.resize(len + 1);
    ::fseek(fd, 0, SEEK_SET);
    auto sz = ::fread((char*)_file_data.c_str(), len, 1, fd);
    ::fclose(fd);
    if (sz != 1)
    {
        printf("ERROR: cannot read correct data of %s, err = %s\n", file_name, strerror(errno));
        return false;
    }
    _file_data[len] = '\n';

    // replace data with arguments
    if (arguments != nullptr)
    {
        std::string str_arguments(arguments);
        std::replace(str_arguments.begin(), str_arguments.end(), ',', ';');
        std::list<std::string> argkvs;
        utils::split_args(str_arguments.c_str(), argkvs, ';');
        for (auto& kv : argkvs)
        {
            std::list<std::string> vs;
            utils::split_args(kv.c_str(), vs, '=');
            if (vs.size() != 2)
            {
                printf("ERROR: invalid configuration argument: '%s' in '%s'\n", kv.c_str(), arguments);
                return false;
            }

            std::string key = std::string("%") + *vs.begin() + std::string("%");
            std::string value = *vs.rbegin();
            _file_data = utils::replace_string(_file_data, key, value);
        }
    }

    //
    // parse mapped file and build conf map
    //
    std::map<std::string, conf*>* pSection = nullptr;
    char *p, *pLine = (char*)"", *pNextLine, *pEnd, *pSectionName = nullptr, *pEqual;
    int lineno = 0;

    // ATTENTION: arguments replace_string() may cause _file_data changed,
    // so set `p' and `pEnd' carefully.
    p = (char*)_file_data.c_str();
    pEnd = p + _file_data.size();

    while (p < pEnd) {
        //
        // get line
        //
        lineno++;
        while (*p == ' ' || *p == '\t' || *p == '\r')    p++;

        pLine = p;
        int shift = 0;
        while (*p != '\n' && p < pEnd)    
        {
            if (*p == '#' || *p == ';')
            {
                if (p != pLine && *(p-1) == '^')
                {
                    shift++;
                }
                else
                {
                    *p = '\0';
                }
            }

            if (shift > 0)
            {
                *(p-shift) = *p;
            }
            p++;
        }
        *(p-shift) = '\0';
        pNextLine = ++p;

        //
        // parse line
        //
        p = pLine;
        if (*p == '\0')    goto Next;    // skip comment line or empty line
        pEqual = strchr(p, '=');
        if (nullptr == pEqual && *p != '[') {
            goto ConfReg;
        }
        if (nullptr != pEqual && *p == '[') 
            goto err;

        //
        //    conf
        //
        if (pEqual) 
        {
ConfReg:
            if (pSection == nullptr) {
                printf("ERROR: configuration section not defined\n");
                goto err;
            }
            if (pEqual)    *pEqual = '\0';
            char* pKey = utils::trim_string(p);
            char* pValue = pEqual ? utils::trim_string(++pEqual) : nullptr;
            if (*pKey == '\0')    
                goto err;

            if (pSection->find((const char*)pKey) != pSection->end()) 
            {
                auto it = pSection->find((const char*)pKey);

                printf("WARNING: skip redefinition of option [%s] %s (line %u), already defined as [%s] %s (line %u)\n",
                    pSectionName,
                    pKey,
                    lineno,
                    it->second->section.c_str(),
                    it->second->key.c_str(),
                    it->second->line
                    );
            }
            else
            {
                conf* cf = new conf;
                cf->section = (const char*)pSectionName;
                cf->key = pKey;                
                cf->line = lineno; 
                cf->present = true;

                if (pValue)
                {
                    // if argument is not provided
                    if (strlen(pValue) > 2 && *pValue == '%' && pValue[strlen(pValue) - 1] == '%')
                        cf->value = "";
                    else
                        cf->value = pValue;
                }
                else
                {
                    cf->value = "";
                }

                pSection->insert(std::make_pair(std::string(pKey), cf));
            }            
        }
        //
        //    section
        //
        else 
        {
            char* pRight = strchr(p, ']');
            if (nullptr == pRight)   
                goto err;
            *pRight = '\0';
            p++;
            pSectionName = utils::trim_string(p);
            if (*pSectionName == '\0')   
                goto err;

            bool old = set_warning(false);
            if (has_section((const char*)pSectionName)) {
                printf("ERROR: configuration section '[%s]' is redefined\n", pSectionName);
                set_warning(old);
                goto err;
            }
            set_warning(old);

            std::map<std::string, conf*> sm;
            auto it = _configs.insert(config_map::value_type(std::string(pSectionName), sm));
            assert (it.second);
            pSection = &it.first->second;
        }

        //
        // iterate nextline
        //
Next:
        p = pNextLine;
    }
    return true;
    
err:
    printf("ERROR: unexpected configuration in %s(line %d): %s\n", file_name, lineno, pLine);
    return false;
}
Beispiel #27
0
int main(int argc, char **argv) {
#define FIND_LONG_OPTION(ARRAY) \
	{															\
		for(i = 0; i < sizeof (ARRAY) / sizeof(struct option); i++) {							\
			struct option *o = (ARRAY) + i;										\
			if(o->arg < 2) {											\
				if(strcmp(arg, o->opt) == 0) {									\
					const char *a = o->arg ? *++v : argv[0];						\
					if(o->arg && !a) {									\
						fprintf(stderr, "%s: option '%s' need an argument\n", argv[0], *v);		\
						return 1;									\
					}											\
					if(o->act) o->act(a);									\
					goto first_loop;									\
				}												\
			} else {												\
				size_t len = strlen(o->opt);									\
				if(strncmp(arg, o->opt, len) == 0) {								\
					if(arg[len] && arg[len] != '=') continue;						\
					if(!arg[len] || !arg[len + 1]) {							\
						fprintf(stderr, "%s: option '%s' need an argument\n", argv[0], *v);		\
						return 1;									\
					}											\
					const char *a = arg + len + 1;								\
					if(o->act) o->act(a);									\
					goto first_loop;									\
				}												\
			}													\
		}														\
	}

#define UNRECOGNIZED_OPTION(O) \
	do {									\
		fprintf(stderr, "%s: error: unrecognized option '%s'\n",	\
			argv[0], (O));						\
		return 1;							\
	} while(0)


	int verbose = 0;
	int no_link = 0;
	int preprocess_only = -1;
	int no_warning = -1;
	int end_of_options = 0;
	const char *output_file = NULL;
	char **v = argv;
	init_argv();

	const char *vs_path = getenv("VS_PATH");
	if(!vs_path) vs_path = getenv("VSINSTALLDIR");
	if(!getenv("INCLUDE")) {
		if(vs_path) {
			size_t len = strlen(vs_path);
			if(vs_path[len - 1] == '/' || vs_path[len - 1] == '\\') len--; 
			char buffer[len + 12 + len + 24 + 1];
			memcpy(buffer, vs_path, len);
			memcpy(buffer + len, "/VC/include;", 12);
			memcpy(buffer + len + 12, vs_path, len);
			strcpy(buffer + len + 12 + len, "/VC/PlatformSDK/include;");
			setenv("INCLUDE", buffer, 0);
		} else {
			no_warning = find_argv(argv, "-w");
			if(!no_warning) fprintf(stderr, "%s: warning: no system include path set\n", argv[0]);
		}
	}
	if(!getenv("LIB")) {
		if(vs_path) {
			size_t len = strlen(vs_path);
			if(vs_path[len - 1] == '/' || vs_path[len - 1] == '\\') len--; 
			char buffer[len + 8 + len + 20 + 1];
			memcpy(buffer, vs_path, len);
			memcpy(buffer + len, "/VC/lib;", 8);
			memcpy(buffer + len + 12, vs_path, len);
			strcpy(buffer + len + 12 + len, "/VC/PlatformSDK/lib;");
			setenv("LIB", buffer, 0);
		} else {
			if(no_warning == -1) no_warning = find_argv(argv, "-w");
			if(!no_warning) fprintf(stderr, "%s: warning: no system library path set\n", argv[0]);
		}
	}

first_loop:
	while(*++v) {
		if(!end_of_options && **v == '-') {
			int i;
			if((*v)[1] == '-') {
				const char *arg = *v + 2;
				if(!*arg) {
					end_of_options = 1;
					continue;
				}
				FIND_LONG_OPTION(double_dash_long_options);
				if(strcmp(arg, "verbose") == 0) {
					verbose = 1;
				} else UNRECOGNIZED_OPTION(*v);
			} else {
				const char *arg = *v + 1;
				FIND_LONG_OPTION(singal_dash_long_options);
				switch(*arg) {
					case 0:
						goto not_an_option;
					case 'c':
						if(arg[1]) UNRECOGNIZED_OPTION(*v);
						add_to_argv("-c");
						no_link = 1;
						break;
					case 'D':
						if(arg[1]) add_to_argv(*v);
						else {
							const char *d = *++v;
							if(!d) {
								fprintf(stderr, "%s: error: macro name missing after '-D'\n",
									argv[0]);
								return 1;
							}
							define(d);
						}
						break;
					case 'E':
						if(arg[1]) UNRECOGNIZED_OPTION(*v);
						add_to_argv("-E");
						preprocess_only = 1;						
						break;
					case 'f':
						if(arg[1]) set_feature(arg + 1);
						else {
							const char *feature = *++v;
							if(!feature) {
								fprintf(stderr, "%s: error: option '-f' need an argument\n",
									argv[0]);
								return -1;
							}
							set_feature(feature);
						}
						break;
					case 'g':
						// -g[coff][<level>] (level: 0~3)
						if(arg[1]) {
							const char *level = arg + 1;
							if(strncmp(level, "coff", 4) == 0) level += 4;
							if(*level && *level != '-') {
								int i = 0;
								do {
									if(!isdigit(level[i])) {
										fprintf(stderr, "%s: error: unrecognized debug output level \"%s\"\n",
											argv[0], level);
										return 1;
									}
								} while(level[++i]);
								/*
								if(i > 1 || *level > '3') {
									fprintf(stderr, "%s: error: debug output level %s is too high\n",
										argv[0], level);
									return 1;
								}
								if(*level == '0') break;
								*/
								int l = atoi(level);
								if(!l) break;
								if(l > 3) {
									fprintf(stderr, "%s: error: debug output level %s is too high\n",
										argv[0], level);
									return 1;
								}
							}
						}
						add_to_argv("-Zi");
						break;
					case 'I':
						if(no_warning == -1) no_warning = find_argv(argv, "-w");
						//if(arg[1]) add_to_argv(*v);
						if(arg[1]) add_include_path(arg + 1, no_warning);
						else {
							const char *path = *++v;
							if(!path) {
								fprintf(stderr, "%s: error: option '-I' need an argument\n",
									argv[0]);
								return 1;
							}
							add_include_path(path, no_warning);
						}
						break;
					case 'L':
						if(no_warning == -1) no_warning = find_argv(argv, "-w");
						if(arg[1]) add_library_path(arg + 1, no_warning);
						else {
							const char *path = *++v;
							if(!path) {
								fprintf(stderr, "%s: error: option '-L' need an argument\n",
									argv[0]);
								return 1;
							}
							add_library_path(path, no_warning);
						}
						break;
					case 'l':
						if(arg[1]) add_library(arg + 1);
						else {
							const char *path = *++v;
							if(!path) {
								fprintf(stderr, "%s: error: option '-l' need an argument\n",
									argv[0]);
								return 1;
							}
							add_library(path);
						}
						break;
					case 'M':
						if(arg[1]) UNRECOGNIZED_OPTION(*v);
						add_to_argv("-showIncludes");
						break;
					case 'm':
						if(arg[1]) set_machine(arg + 1);
						else {
							const char *machine = *++v;
							if(!machine) {
								fprintf(stderr, "%s: argument to `-m' is missing\n",
									argv[0]);
								return 1;
							}
							set_machine(machine);
						}
						break;
					case 'O':
						if(arg[1]) {
							const char *o = arg + 1;
							if(strcmp(o, "0") == 0) add_to_argv("-Od");
							else if(strcmp(o, "1") == 0) add_to_argv("-O2");
							else if(strcmp(o, "3") == 0) add_to_argv("-Ox");
							else if(strcmp(o, "s") == 0) add_to_argv("-O1");
							else if(strcmp(o, "fast") == 0) add_to_argv("-O2");
							else add_to_argv(*v);
						} else add_to_argv("-O2");
						break;
					case 'o':
						if(arg[1]) output_file = arg + 1;
						else {
							output_file = *++v;
							if(!output_file) {
								fprintf(stderr, "%s: error: option '-o' need an argument\n",
									argv[0]);
								return 1;
							}
						}
						break;
					case 'P':
						if(preprocess_only == -1) preprocess_only = find_argv(argv, "-E");
						if(preprocess_only) add_to_argv("-EP");
						break;
					case 's':
						if(arg[1]) UNRECOGNIZED_OPTION(*v);
						break;
					case 'U':
						if(arg[1]) add_to_argv(*v);
						else {
							const char *u = *++v;
							if(!u) {
								fprintf(stderr, "%s: error: macro name missing after '-U'\n",
									argv[0]);
								return 1;
							}
							undefine(u);
						}
						break;
					case 'v':
						if(arg[1]) UNRECOGNIZED_OPTION(*v);
						verbose = 1;
						break;
					case 'W':
						if(!arg[1]) {
							if(no_warning == -1) no_warning = find_argv(argv, "-w");
							if(!no_warning) {
								fprintf(stderr, "%s: warning: option '-W' is deprecated; use '-Wextra' instead\n",
									argv[0]);
							}
							add_to_argv("-Wall");
							break;
						}
						if(strncmp(arg, "Wa,", 3) == 0 || strncmp(arg, "Wp,", 3) == 0 || strncmp(arg, "Wl,", 3) == 0) {
							(*v)[3] = 0;		// XXX
							fprintf(stderr, "%s: warning: option '%s' is not supported\n", argv[0], *v);
							break;
						} 
						if(set_warning(arg + 1)) break;
						add_to_argv(*v);
						break;
					case 'w':
						if(arg[1]) UNRECOGNIZED_OPTION(*v);
						add_to_argv("-w");
						no_warning = 1;
						break;
					case 'x':
						if(arg[1]) set_language(arg + 1);
						else {
							const char *lang = *++v;
							if(!lang) {
								fprintf(stderr, "%s: error: missing argument to ‘-x’",
									argv[0]);
								return 4;
							}
							set_language(lang);
						}
						break;
					default:
						fprintf(stderr, "%s: error: unrecognized option '%s'\n", argv[0], *v);
						return 1;
				}
			}
		} else {
not_an_option:
#if defined __INTERIX && !defined _NO_CONV_PATH
			if(**v == '/') {
				char buffer[PATH_MAX + 1];
				if(unixpath2win(*v, 0, buffer, sizeof buffer) == 0) {
					add_input_file(buffer);
				} else {
					if(no_warning == -1) no_warning = find_argv(argv, "-w");
					if(!no_warning) {
						fprintf(stderr, "%s: warning: cannot convert '%s' to Windows path name, %s\n",
							argv[0], *v, strerror(errno));
					}
					add_input_file(*v);
				}
			} else
#endif
			add_input_file(*v);
		}
	}
	setvbuf(stdout, NULL, _IOLBF, 0);
	if(preprocess_only == -1) preprocess_only = 0;
	if(no_warning == -1) no_warning = 0;
	if(last_language && last_language_unused && !no_warning) {
		fprintf(stderr, "%s: warning: '-x %s' after last input file has no effect\n", argv[0], last_language);
	}
	if(!first_input_file) {
		if(verbose) {
			if(!no_link) add_to_argv("-c");
			start_cl();
			return 0;
		}
		fprintf(stderr, "%s: no input files\n", argv[0]);
		return 1;
	}
	if(multiple_input_files && (preprocess_only || no_link)) {
		if(output_file) {
			fprintf(stderr, "%s: error: cannot specify -o with -c or -E with multiple files\n", argv[0]);
			return 4;
		} else if(no_link) {
			fprintf(stderr, "%s: error: '-c' with multiple files is currently not supported\n", argv[0]);
			return -1;
		}
	}
	if(!output_file && !preprocess_only) {
		if(no_link) {
			size_t len = strlen(first_input_file);
			int n = get_last_dot(first_input_file, len);
			if(n >= 0) len = n;
			char *p = malloc(len + 3);
			if(!p) {
				perror(argv[0]);
				return 1;
			}
			memcpy(p, first_input_file, len);
			strcpy(p + len, ".o");
			output_file = p;
		} else output_file = DEFAULT_OUTPUT_FILENAME;
	}
	if(!verbose) add_to_argv("-nologo");
	if(preprocess_only) {
		if(output_file) target.name = output_file;
		target.type = PREPROCESSED_SOURCE;
	} else set_output_file(output_file, no_link, no_warning);
	//if(no_static_link) add_to_argv("-MD");
	add_to_argv(no_static_link ? "-MD" : "-MT");
	add_libraries_to_argv();
	if(verbose) print_argv();
	return start_cl();
}