Example #1
0
bool ConfigFile::SetSource(const char* file, bool /*ignorecase*/)
{
    /* wipe any existing settings. */
    m_settings.clear();

    /* open the file */
    if (file != 0)
    {
        //the right mode in Windows is "rb" since '\n' is saved as 0x0D,0x0A but fopen(file,"r") reads these 2 chars
        //as only 1 char, so ftell(f) returns a higher value than the required by fread() to the file to buf.
#ifdef WIN32
        FILE* f = fopen(file, "rb");
#else
        FILE* f = fopen(file, "r");
#endif
        char* buf;
        int length;
        if (!f)
        {
            sLog.outError("Could not open %s.", file);
            return false;
        }

        /* get the length of the file */
        fseek(f, 0, SEEK_END);
        length = ftell(f);
        buf = new char[length + 1];
        fseek(f, 0, SEEK_SET);

        fread(buf, length, 1, f);
        buf[length] = '\0';
        string buffer = string(buf);
        delete[] buf;

        /* close the file, it is no longer needed */
        fclose(f);

        /* let's parse it. */
        string line;
        string::size_type end;
        string::size_type offset;
        bool in_multiline_comment = false;
        bool in_multiline_quote = false;
        bool in_block = false;
        string current_setting = "";
        string current_variable = "";
        string current_block = "";
        ConfigBlock current_block_map;
        ConfigSetting current_setting_struct;

        /* oh god this is awful */
        try
        {
            for (;;)
            {
                /* grab a line. */
                end = buffer.find(EOL);
                if (end == string::npos)
                {
                    if (buffer.size() == 0)
                        break;
                    line = buffer;
                    buffer.clear();
                    goto parse;
                }

                line = buffer.substr(0, end);
                buffer.erase(0, end + EOL_SIZE);
                goto parse;

            parse:
                if (!line.size())
                    continue;

                /* are we a comment? */
                if (!in_multiline_comment && is_comment(line, &in_multiline_comment))
                {
                    /* our line is a comment. */
                    if (!in_multiline_comment)
                    {
                        /* the entire line is a comment, skip it. */
                        continue;
                    }
                }

                /* handle our cases */
                if (in_multiline_comment)
                {
                    // we need to find a "*/".
                    offset = line.find("*/", 0);

                    /* skip this entire line, eh? */
                    if (offset == string::npos)
                        continue;

                    /* remove up to the end of the comment block. */
                    line.erase(0, offset + 2);
                    in_multiline_comment = false;
                }

                if (in_block)
                {
                    /* handle settings across multiple lines */
                    if (in_multiline_quote)
                    {
                        /* attempt to find the end of the quote block. */
                        offset = line.find("\"");

                        if (offset == string::npos)
                        {
                            /* append the whole line to the quote. */
                            current_setting += line;
                            current_setting += "\n";
                            continue;
                        }

                        /* only append part of the line to the setting. */
                        current_setting.append(line.c_str(), offset + 1);
                        line.erase(0, offset + 1);

                        /* append the setting to the config block. */
                        if (current_block == "" || current_variable == "")
                        {
                            sLog.outError("Quote without variable.");
                            return false;
                        }

                        /* apply the setting */
                        apply_setting(current_setting, current_setting_struct);

                        /* the setting is done, append it to the current block. */
                        current_block_map[ahash(current_variable)] = current_setting_struct;
#ifdef _CONFIG_DEBUG
                        sLog.outDebug("Block: '%s', Setting: '%s', Value: '%s'", current_block.c_str(), current_variable.c_str(), current_setting_struct.AsString.c_str());
#endif
                        /* no longer doing this setting, or in a quote. */
                        current_setting = "";
                        current_variable = "";
                        in_multiline_quote = false;
                    }

                    /* remove any leading spaces */
                    remove_spaces(line);

                    if (!line.size())
                        continue;

                    /* our target is a *setting*. look for an '=' sign, this is our seperator. */
                    offset = line.find("=");
                    if (offset != string::npos)
                    {
                        ASSERT(current_variable == "");
                        current_variable = line.substr(0, offset);

                        /* remove any spaces from the end of the setting */
                        remove_all_spaces(current_variable);

                        /* remove the directive *and* the = from the line */
                        line.erase(0, offset + 1);
                    }

                    /* look for the opening quote. this signifies the start of a setting. */
                    offset = line.find("\"");
                    if (offset != string::npos)
                    {
                        ASSERT(current_setting == "");
                        ASSERT(current_variable != "");

                        /* try and find the ending quote */
                        end = line.find("\"", offset + 1);
                        if (end != string::npos)
                        {
                            /* the closing quote is on the same line, oh goody. */
                            current_setting = line.substr(offset + 1, end - offset - 1);

                            /* erase up to the end */
                            line.erase(0, end + 1);

                            /* apply the setting */
                            apply_setting(current_setting, current_setting_struct);

                            /* the setting is done, append it to the current block. */
                            current_block_map[ahash(current_variable)] = current_setting_struct;

#ifdef _CONFIG_DEBUG
                            sLog.outDebug("Block: '%s', Setting: '%s', Value: '%s'", current_block.c_str(), current_variable.c_str(), current_setting_struct.AsString.c_str());
#endif
                            /* no longer doing this setting, or in a quote. */
                            current_setting = "";
                            current_variable = "";
                            in_multiline_quote = false;

                            /* attempt to grab more settings from the same line. */
                            goto parse;
                        }
                        else
                        {
                            /* the closing quote is not on the same line. means we'll try and find it on
                               the next. */
                            current_setting.append(line.c_str(), offset);

                            /* skip to the next line. (after setting our condition first, of course :P */
                            in_multiline_quote = true;
                            continue;
                        }
                    }

                    /* are we at the end of the block yet? */
                    offset = line.find(">");
                    if (offset != string::npos)
                    {
                        line.erase(0, offset + 1);

                        // freeeee!
                        in_block = false;

                        /* assign this block to the main "big" map. */
                        m_settings[ahash(current_block)] = current_block_map;

                        /* erase all data for this so it doesn't seep through */
                        current_block_map.clear();
                        current_setting = "";
                        current_variable = "";
                        current_block = "";
                    }
                }
                else
                {
                    /* we're not in a block. look for the start of one. */
                    offset = line.find("<");

                    if (offset != string::npos)
                    {
                        in_block = true;

                        /* whee, a block! let's cut the string and re-parse. */
                        line.erase(0, offset + 1);

                        /* find the name of the block first, though. */
                        offset = line.find(" ");
                        if (offset != string::npos)
                        {
                            current_block = line.substr(0, offset);
                            line.erase(0, offset + 1);
                        }
                        else
                        {
                            sLog.outError("Block without name.");
                            return false;
                        }

                        /* skip back */
                        goto parse;
                    }
                }
            }
        }
        catch (...)
        {
            sLog.outError("Exception in config parsing.");
            return false;
        }

        /* handle any errors */
        if (in_block)
        {
            sLog.outError("Unterminated block.");
            return false;
        }

        if (in_multiline_comment)
        {
            sLog.outError("Unterminated comment.");
            return false;
        }

        if (in_multiline_quote)
        {
            sLog.outError("Unterminated quote.");
            return false;
        }

        /* we're all good :) */
        return true;
    }

    return false;
}
Example #2
0
program_t parse(string fn)
{
	int n_osc=0;
	oscillator_t *osc=NULL;
	env_settings_t *env=NULL;
	filter_params_t filter;
	fixed_t sync_factor=0;
	bool sync_factor_const=true;
	
	char buf[2000];
	string line;
	string var;
	string array;
	string strval;
	float val;
	
	parameter_enum p;
	
	int ind,ind2=0;
	
	int state;

	program_t result;

	
	ifstream f;
	f.open(fn.c_str());
	if (f.good())
	{
		state=0;
		while (!f.eof())
		{
			f.getline(buf,sizeof(buf)/sizeof(*buf)-1);
			line=buf;
			line=remove_all_spaces(buf);
			if ((line!="") && (line[0]!='#')) //ignore comments and empty lines
			{
				if (line=="controllers:")
				{
					state=2;
					continue;
				}
				else if (line=="defaults:")
				{
					state=3;
					continue;
				}
				else if (line=="velocity:")
				{
					state=4;
					continue;
				}
				else if (line=="variable:")
				{
					state=5;
					continue;
				}

				var=extract_var(line);
				array=extract_array_name(var);
				strval=extract_val(line);
				val=atof(strval.c_str());
				
				switch (state)
				{
					case 0: //expect and read number of oscillators
						if (var!="oscillators")
							throw string("need to know number of oscillators");
						else
							n_osc=val;
						
						if (n_osc<=0) throw string("invalid number of oscillators");
						
						//init stuff
						env=new env_settings_t[n_osc];
						osc=new oscillator_t[n_osc];
						init_oscs(n_osc, osc);
						init_envs(n_osc, env);
						init_filter(filter);
						
						state=1;
						break;
					
					case 1: //read and set information about oscillator settings
						p=param_to_enum(array);
						
						ind=extract_array_index(var,1);
						if ( param_needs_index(p) &&  (!((ind>=0) && (ind<n_osc))) )
							throw string("out of array bounds");
						
						
						switch (p)
						{
							case MODULATION:						
								ind2=extract_array_index(var,2);
								if (!((ind2>=0) && (ind2<n_osc)))
									throw string("out of array bounds");
							
								osc[ind].fm_strength[ind2]=val*ONE;
								break;
							case OUTPUT:
								osc[ind].output=val*ONE;
								break;
							case WAVEFORM:
								if (isfloat(strval))
								{
									osc[ind].waveform=int(val);
								}
								else
								{
									osc[ind].have_custom_wave=true;
								}
								break;
							case FACTOR:
								osc[ind].factor=val*ONE;
								break;
							case TREMOLO:
								osc[ind].tremolo_depth=int(val);
								break;
							case TREM_LFO:
								if (strval=="snh")
									osc[ind].tremolo_lfo=SNH_LFO;
								else
								{
									osc[ind].tremolo_lfo= int(val);
									if ((val<0) || (val>=N_LFOS))
										throw string("invalid value for tremolo_lfo");
								}
								break;
							case VIBRATO:
								osc[ind].vibrato_depth=val;
								break;
							case VIB_LFO:
								if (strval=="snh")
									osc[ind].vibrato_lfo= SNH_LFO;
								else
								{
									osc[ind].vibrato_lfo= int(val);
									if ((val<0) || (val>=N_LFOS))
										throw string("invalid value for vibrato_lfo");
								}
								break;
							case ATTACK:
								env[ind].attack=val;
								break;
							case DECAY:
								env[ind].decay=val;
								break;
							case SUSTAIN:
								env[ind].sustain=val;
								break;
							case RELEASE:
								env[ind].release=val;
								break;
							case HOLD:
								env[ind].hold=(val!=0);
								break;
							case KSR:
								osc[ind].ksr=val;
								break;
							case KSL:
								osc[ind].ksl=val;
								break;
							case SYNC:
								osc[ind].sync=(val!=0);
								break;
							case FILTER_ENABLED:
								filter.enabled=(val!=0);
								break;
							case FILTER_ENV_AMOUNT:
								filter.env_amount=val;
								break;
							case FILTER_ATTACK:
								filter.env_settings.attack=val;
								break;
							case FILTER_DECAY:
								filter.env_settings.decay=val;
								break;
							case FILTER_SUSTAIN:
								filter.env_settings.sustain=val;
								break;
							case FILTER_RELEASE:
								filter.env_settings.release=val;
								break;
							case FILTER_HOLD:
								filter.env_settings.hold=(val!=0);
								break;
							case FILTER_OFFSET:
								filter.freqfactor_offset=val;
								break;
							case FILTER_RESONANCE:
								filter.resonance=val;
								break;
							case FILTER_TREMOLO:
								filter.trem_strength=int(val);
								break;
							case FILTER_TREM_LFO:
								if (strval=="snh")
									filter.trem_lfo=SNH_LFO;
								else
								{
									filter.trem_lfo=int(val);
									if ((val<0) || (val>=N_LFOS))
										throw string("invalid value for filter_trem_lfo");
								}
								break;
							case SYNC_FACTOR:
								sync_factor=val*ONE;
								break;
							default:
								throw string("unknown variable ('"+array+"')");
						}
						break;
					
					case 5: //read which params shall be variable, even if
					        //there are currently no controllers which change them
					case 4: //read velocity-influence over certain params
					case 2: //read how controllers influence parameters
						p=param_to_enum(array);
						
						ind=extract_array_index(var,1);
						if ( param_needs_index(p) &&  (!((ind>=0) && (ind<n_osc))) )
							throw string("out of array bounds");
						
						if (state==4) //velocity-influence
						{
							switch (p)
							{
								case MODULATION:
								case OUTPUT:
								case FILTER_ENV_AMOUNT:
								case FILTER_RESONANCE:
								case FILTER_OFFSET:
									// everything ok, do nothing
									break;
								
								default: // other params than the above may not be influenced!
									throw string("velocity cannot influence parameter '"+array+"'");
							}
						}
						

						switch (p)
						{
							case MODULATION:						
								ind2=extract_array_index(var,2);
								if (!((ind2>=0) && (ind2<n_osc)))
									throw string("out of array bounds");
							
								osc[ind].fm_strength_const[ind2]=false;
								break;
							case OUTPUT:
								if (state!=4) // not vel.-influence
									osc[ind].output_const=false;
								break;
							case WAVEFORM:
								osc[ind].waveform_const=false; break;
							case FACTOR:
								osc[ind].factor_const=false; break;
							case TREMOLO:
								osc[ind].tremolo_depth_const=false; break;
							case TREM_LFO:
								osc[ind].tremolo_lfo_const=false; break;
							case VIBRATO:
								osc[ind].vibrato_depth_const=false; break;
							case VIB_LFO:
								osc[ind].vibrato_lfo_const=false; break;
							case ATTACK:
								env[ind].attack_const=false; break;
							case DECAY:
								env[ind].decay_const=false; break;
							case SUSTAIN:
								env[ind].sustain_const=false; break;
							case RELEASE:
								env[ind].release_const=false; break;
							case HOLD:
								env[ind].hold_const=false; break;
							case KSR:
								osc[ind].ksr_const=false; break;
							case KSL:
								osc[ind].ksl_const=false; break;
							case SYNC:
								osc[ind].sync_const=false; break;
							case FILTER_ENABLED:
								filter.enabled_const=false; break;
							case FILTER_ENV_AMOUNT:
								filter.env_amount_const=false; break;
							case FILTER_ATTACK:
								filter.env_settings.attack_const=false; break;
							case FILTER_DECAY:
								filter.env_settings.decay_const=false; break;
							case FILTER_SUSTAIN:
								filter.env_settings.sustain_const=false; break;
							case FILTER_RELEASE:
								filter.env_settings.release_const=false; break;
							case FILTER_HOLD:
								filter.env_settings.hold_const=false; break;
							case FILTER_OFFSET:
								filter.freqfactor_offset_const=false; break;
							case FILTER_RESONANCE:
								filter.resonance_const=false; break;
							case FILTER_TREMOLO:
								filter.trem_strength_const=false; break;
							case FILTER_TREM_LFO:
								filter.trem_lfo_const=false; break;
							case SYNC_FACTOR:
								sync_factor_const=false; break;
							default:
								throw string("unknown variable ('"+array+"')");
						}


						
						break;
					
					case 3: //read controller default values
						//ignored						
						break;
				}
			}
		}
		
		
		//some optimizations and checks
		
		for (int i=0;i<n_osc;i++)
			if ((env[i].attack==0) && (env[i].sustain==1.0)
			      && (env[i].release>100))  //TODO FINDMICH besseres kriterium?
				env[i].enabled=false;
		
		if (  ((filter.env_settings.attack==0) && (filter.env_settings.sustain==1.0)
		        && (filter.env_settings.release>100)) //TODO FINDMICH siehe oben
		   || ((filter.env_amount==0) && (filter.env_amount_const==true))  )
		  filter.env_settings.enabled=false;
		
		bool use_sync=false;
		for (int i=0;i<n_osc;i++)
			if ((osc[i].sync==true) || (osc[i].sync_const==false))
			{
				use_sync=true;
				break;
			}
		
		if (!use_sync)
		{
			sync_factor=0;
			sync_factor_const=true;
		}
		
		if ((sync_factor==0) && (sync_factor_const==true))
			for (int i=0;i<n_osc;i++)
			{
				osc[i].sync=false;
				osc[i].sync_const=true;
			}
		
		
		
		for (int i=0;i<n_osc;i++)
			if ( !((osc[i].output==0) && (osc[i].output_const=true)) )
				osc[i].output_const=false;
		
		//end optimizations and checks
		
		result.n_osc=n_osc;
		result.osc=osc;
		result.env=env;
		result.filter=filter;
		result.sync_factor=sync_factor;
		result.sync_factor_const=sync_factor_const;
	}
	else
		throw string ("could not open '"+fn+"'");

	return result;
	//no uninit / free of osc and env here, as it must be done by the caller
}