Beispiel #1
0
/* -------------------------------------------------------------------
 * Provide options to a pSQL environment - If NULL the use the default
 * ------------------------------------------------------------------- */
void jx_sqlSetOptions (PJXNODE pOptionsP)
{

   PJXSQLCONNECT pc = jx_getCurrentConnection();
   PSQLOPTIONS po = &pConnection->options;
   PJXNODE pNode;

   // Delete previous settings, if we did that parsing
   if (pConnection->pOptionsCleanup) {
       jx_Close(&pConnection->pOptions);
   }

   // .. and set the new setting
   pConnection->pOptionsCleanup = false;
   if (ON == jx_isNode(pOptionsP)) {
       pConnection->pOptions = pOptionsP;
   } else if (pOptionsP != NULL) {
       pConnection->pOptions = jx_ParseString ((PUCHAR) pOptionsP , NULL);
       pConnection->pOptionsCleanup = true;
   }

   pNode    =  jx_GetNode(pConnection->pOptions, "/");
   while (pNode) {
      int rc = SQL_SUCCESS;
      PUCHAR name, value;
      LONG attrParm;
      name  = jx_GetNodeNamePtr   (pNode);
      value = jx_GetNodeValuePtr  (pNode , NULL);

      // Is header overriden by userprogram ?
      if (BeginsWith(name , "upperCaseColName")) {
         po->upperCaseColName = *value == 't'? ON:OFF; // for true
      }
      else if (BeginsWith(name , "autoParseContent")) {
         po->autoParseContent = *value == 't' ? ON:OFF; // for true
      }
      else if (BeginsWith(name , "DecimalPoint")) {
         po->DecimalPoint = *value;
      }
      else if (BeginsWith(name , "sqlNaming")) {
         po->sqlNaming = *value == 't' ? ON:OFF; // for true
         attrParm = po->sqlNaming == OFF; // sysname is invers of SQL naming :(
         rc = SQLSetConnectAttr     (pConnection->hdbc , SQL_ATTR_DBC_SYS_NAMING, &attrParm  , 0);
      }
      if (rc  != SQL_SUCCESS ) {
        check_error (NULL);
        return ; // we have an error
      }

      /* more to come....
         po->hexSort;
         po->DateSep;
         po->DateFmt;
         po->TimeSep;
         po->TimeFmt;
         po->DecimalPoint;
      */
      pNode = jx_GetNodeNext(pNode);
   }
}
Beispiel #2
0
char *XmlGetNodeText (char *xmlNode, char *xmlText, int xmlTextSize)
{
	char *t = xmlNode;
	char *e = xmlNode + 1;
	int l = 0, i = 0, j = 0;

	xmlText[0] = 0;

	if (t[0] != '<')
		return NULL;

	t = (char*) strchr (t, '>');
	if (t == NULL) return NULL;

	t++;
	e = strchr (e, '<');
	if (e == NULL) return NULL;

	l = (int)(e - t);
	if (e == NULL || l > xmlTextSize) return NULL;

	while (i < l)
	{
		if (BeginsWith (&t[i], "&lt;"))
		{
			xmlText[j++] = '<';
			i += 4;
			continue;
		}
		if (BeginsWith (&t[i], "&gt;"))
		{
			xmlText[j++] = '>';
			i += 4;
			continue;
		}
		if (BeginsWith (&t[i], "&amp;"))
		{
			xmlText[j++] = '&';
			i += 5;
			continue;
		}
		xmlText[j++] = t[i++];
	}
	xmlText[j] = 0;

	return t;
}
Beispiel #3
0
static void
TestNamePrefixVisitor(const Waypoints &waypoints, const TCHAR *prefix,
                      unsigned expected_results)
{
  WaypointPredicateCounter::Predicate predicate = BeginsWith(prefix);
  WaypointPredicateCounter prefix_counter(predicate);
  waypoints.VisitNamePrefix(prefix, prefix_counter);
  ok1(prefix_counter.GetCounter() == expected_results);
}
Beispiel #4
0
/* ------------------------------------------------------------- */
LGL jx_sqlUpdate (PUCHAR table  , PJXNODE pSqlParms , PUCHAR whereP)
{
   PNPMPARMLISTADDRP pParms = _NPMPARMLISTADDR();
   PUCHAR where =  (pParms->OpDescList->NbrOfParms >= 3) ? whereP : "";
   UCHAR  whereStr [1024];
   for(; *where == ' ' ; where++); // skip leading blanks
   if (*where > ' ' && ! BeginsWith(where, "where")) {
       sprintf (whereStr , "where %s" , where);
       where = whereStr;
   }
   return jx_sqlUpsert  (true , table  , pSqlParms , where);
}
Beispiel #5
0
char *XmlGetAttributeText (char *xmlNode, char *xmlAttrName, char *xmlAttrValue, int xmlAttrValueSize)
{
	char *t = xmlNode;
	char *e = xmlNode;
	int l = 0;

	xmlAttrValue[0] = 0;
	if (t[0] != '<') return NULL;

	e = strchr (e, '>');
	if (e == NULL) return NULL;

	while ((t = strstr (t, xmlAttrName)) && t < e)
	{
		char *o = t + strlen (xmlAttrName);
		if (t[-1] == ' '
			&&
			(BeginsWith (o, "=\"")
			|| BeginsWith (o, "= \"")
			|| BeginsWith (o, " =\"")
			|| BeginsWith (o, " = \""))
			)
			break;

		t++;
	}

	if (t == NULL || t > e) return NULL;

	t = strchr (t, '"') + 1;
	e = strchr (t, '"');
	l = (int)(e - t);
	if (e == NULL || l > xmlAttrValueSize) return NULL;

	memcpy (xmlAttrValue, t, l);
	xmlAttrValue[l] = 0;

	return xmlAttrValue;
}
Beispiel #6
0
static bool
handle_privmsg(int fd, struct ircmsg_privmsg *msg)
{
    // Only handle messages directed at the bot.
    if (strncmp(msg->text, IRC_NICK, strlen(IRC_NICK)) != 0)
        return true;

    // Only handle messages in a channel.
    if (msg->chan[0] != '#')
        return true;

    char *cmd = msg->text + strlen(IRC_NICK ": ");

    if (BeginsWith(cmd, "help"))
        return handle_cmd_help(fd, msg, head + 4);
    if (BeginsWith(cmd, "record "))
        return handle_cmd_record(fd, msg, head + 7);
    if (BeginsWith(cmd, "records "))
        return handle_cmd_records(fd, msg, head + 8);

    irc_privmsg(fd, msg->chan, "%s: shut the f**k up.", msg->name.nick);
    return true;
}
Beispiel #7
0
char *XmlFindElement (char *xmlNode, char *nodeName)
{
	char *t = xmlNode;
	size_t nameLen = strlen (nodeName);

	do
	{
		if (BeginsWith (t + 1, nodeName)
			&& (t[nameLen + 1] == '>'
			|| t[nameLen + 1] == ' ')) return t;

	} while (t = XmlNextNode (t));

	return NULL;
}
void DirectFilenameDB::PopulateFileSet( FileSet &fs, const RString &path )
{
	RString sPath = path;

#if defined(XBOX)
	/* Xbox doesn't handle path names which end with ".", which are used when using an
	 * alternative song directory */
	if( sPath.size() > 0 && sPath.Right(1) == "." )
		sPath.erase( sPath.size() - 1 );
#endif

	/* Resolve path cases (path/Path -> PATH/path). */
	ResolvePath( sPath );

	fs.age.GetDeltaTime(); /* reset */
	fs.files.clear();

#if defined(WIN32)
	WIN32_FIND_DATA fd;

	if ( sPath.size() > 0  && sPath.Right(1) == "/" )
		sPath.erase( sPath.size() - 1 );

	HANDLE hFind = DoFindFirstFile( root+sPath+"/*", &fd );
	CHECKPOINT_M( root+sPath+"/*" );

	if( hFind == INVALID_HANDLE_VALUE )
		return;

	do {
		if( !strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, "..") )
			continue;

		File f;
		f.SetName( fd.cFileName );
		f.dir = !!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
		f.size = fd.nFileSizeLow;
		f.hash = fd.ftLastWriteTime.dwLowDateTime;

		fs.files.insert( f );
	} while( FindNextFile( hFind, &fd ) );
	FindClose( hFind );
#else
	/* Ugly: POSIX threads are not guaranteed to have per-thread cwds, and only
	 * a few systems have openat() or equivalent; one or the other is needed
	 * to do efficient, thread-safe directory traversal.  Instead, we have to
	 * use absolute paths, which forces the system to re-parse the directory
	 * for each file.  This isn't a major issue, since most large directory
	 * scans are I/O-bound. */
	 
	DIR *pDir = opendir(root+sPath);
	if( pDir == NULL )
		return;

	while( struct dirent *pEnt = readdir(pDir) )
	{
		if( !strcmp(pEnt->d_name, ".") )
			continue;
		if( !strcmp(pEnt->d_name, "..") )
			continue;
		
		File f( pEnt->d_name );
		
		struct stat st;
		if( DoStat(root+sPath + "/" + pEnt->d_name, &st) == -1 )
		{
			int iError = errno;
			/* If it's a broken symlink, ignore it.  Otherwise, warn. */
			if( lstat(root+sPath + "/" + pEnt->d_name, &st) == 0 )
				continue;
			
			/* Huh? */
			WARN( ssprintf("Got file '%s' in '%s' from list, but can't stat? (%s)",
					pEnt->d_name, sPath.c_str(), strerror(iError)) );
			continue;
		}
		else
		{
			f.dir = (st.st_mode & S_IFDIR);
			f.size = (int)st.st_size;
			f.hash = st.st_mtime;
		}

		fs.files.insert(f);
	}
	       
	closedir( pDir );
#endif

	/*
	 * Check for any ".ignore" markers.  If a marker exists, hide the marker and its 
	 * corresponding file.
	 * For example, if "file.xml.ignore" exists, hide both it and "file.xml" by 
	 * removing them from the file set.
	 * Ignore markers are used for convenience during build staging and are not used in 
	 * performance-critical situations.  To avoid incurring some of the overheard 
	 * due to ignore markers, delete the file instead instead of using an ignore marker.
	 */
	static const RString IGNORE_MARKER_BEGINNING = "ignore-";

	vector<RString> vsFilesToRemove;
	for( set<File>::iterator iter = fs.files.lower_bound(IGNORE_MARKER_BEGINNING); 
		 iter != fs.files.end(); 
		 ++iter )
	{
		if( !BeginsWith( iter->lname, IGNORE_MARKER_BEGINNING ) )
			break;
		RString sFileLNameToIgnore = iter->lname.Right( iter->lname.length() - IGNORE_MARKER_BEGINNING.length() );
		vsFilesToRemove.push_back( iter->name );
		vsFilesToRemove.push_back( sFileLNameToIgnore );
	}
	
	FOREACH_CONST( RString, vsFilesToRemove, iter )
	{
		// Erase the file corresponding to the ignore marker
		File fileToDelete;
		fileToDelete.SetName( *iter );
		set<File>::iterator iter2 = fs.files.find( fileToDelete );
		if( iter2 != fs.files.end() )
			fs.files.erase( iter2 );
	}
Beispiel #9
0
static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant )
{
	MsdFile msd;
	if( !msd.ReadFile( sPath, false ) )  // don't unescape
	{
		LOG->UserLog( "Song file", sPath, "couldn't be opened: %s", msd.GetError().c_str() );
		return false;
	}

	// changed up there in case of something is found inside the SONGFILE tag in the head ksf -DaisuMaster
	// search for music with song in the file name
	vector<RString> arrayPossibleMusic;
	GetDirListing( out.GetSongDir() + RString("song.mp3"), arrayPossibleMusic );
	GetDirListing( out.GetSongDir() + RString("song.oga"), arrayPossibleMusic );
	GetDirListing( out.GetSongDir() + RString("song.ogg"), arrayPossibleMusic );
	GetDirListing( out.GetSongDir() + RString("song.wav"), arrayPossibleMusic );

	if( !arrayPossibleMusic.empty() )		// we found a match
		out.m_sMusicFile = arrayPossibleMusic[0];
	// ^this was below, at the end

	float SMGap1 = 0, SMGap2 = 0, BPM1 = -1, BPMPos2 = -1, BPM2 = -1, BPMPos3 = -1, BPM3 = -1;
	int iTickCount = -1;
	bKIUCompliant = false;
	vector<RString> vNoteRows;

	for( unsigned i=0; i < msd.GetNumValues(); i++ )
	{
		const MsdFile::value_t &sParams = msd.GetValue(i);
		RString sValueName = sParams[0];
		sValueName.MakeUpper();

		// handle the data
		if( sValueName=="TITLE" )
			LoadTags(sParams[1], out);
		else if( sValueName=="BPM" )
		{
			BPM1 = StringToFloat(sParams[1]);
			out.m_SongTiming.AddSegment( BPMSegment(0, BPM1) );
		}
		else if( sValueName=="BPM2" )
		{
			bKIUCompliant = true;
			BPM2 = StringToFloat( sParams[1] );
		}
		else if( sValueName=="BPM3" )
		{
			bKIUCompliant = true;
			BPM3 = StringToFloat( sParams[1] );
		}
		else if( sValueName=="BUNKI" )
		{
			bKIUCompliant = true;
			BPMPos2 = StringToFloat( sParams[1] ) / 100.0f;
		}
		else if( sValueName=="BUNKI2" )
		{
			bKIUCompliant = true;
			BPMPos3 = StringToFloat( sParams[1] ) / 100.0f;
		}
		else if( sValueName=="STARTTIME" )
		{
			SMGap1 = -StringToFloat( sParams[1] )/100;
			out.m_SongTiming.m_fBeat0OffsetInSeconds = SMGap1;
		}
		// This is currently required for more accurate KIU BPM changes.
		else if( sValueName=="STARTTIME2" )
		{
			bKIUCompliant = true;
			SMGap2 = -StringToFloat( sParams[1] )/100;
		}
		else if ( sValueName=="STARTTIME3" )
		{
			// STARTTIME3 only ensures this is a KIU compliant simfile.
			//bKIUCompliant = true;
		}
		else if ( sValueName=="TICKCOUNT" )
		{
			ProcessTickcounts(sParams[1], iTickCount, out.m_SongTiming);
		}
		else if ( sValueName=="STEP" )
		{
			/* STEP will always be the last header in a KSF file by design. Due to
			 * the Direct Move syntax, it is best to get the rows of notes here. */
			RString theSteps = sParams[1];
			TrimLeft( theSteps );
			split( theSteps, "\n", vNoteRows, true );
		}
		else if( sValueName=="DIFFICULTY" || sValueName=="PLAYER" )
		{
			/* DIFFICULTY and PLAYER are handled only in LoadFromKSFFile.
			Ignore those here. */
			continue;
		}
		// New cases noted in Aldo_MX's code:
		else if( sValueName=="MUSICINTRO" || sValueName=="INTRO" )
		{
			out.m_fMusicSampleStartSeconds = HHMMSSToSeconds( sParams[1] );
		}
		else if( sValueName=="TITLEFILE" )
		{
			out.m_sBackgroundFile = sParams[1];
		}
		else if( sValueName=="DISCFILE" )
		{
			out.m_sBannerFile = sParams[1];
		}
		else if( sValueName=="SONGFILE" )
		{
			out.m_sMusicFile = sParams[1];
		}
		//else if( sValueName=="INTROFILE" )
		//{
		//	nothing to add...
		//}
		// end new cases
		else
		{
			LOG->UserLog( "Song file", sPath, "has an unexpected value named \"%s\".",
				      sValueName.c_str() );
		}
	}

	//intro length in piu mixes is generally 7 seconds
	out.m_fMusicSampleLengthSeconds = 7.0f;

	/* BPM Change checks are done here.  If bKIUCompliant, it's short and sweet.
	 * Otherwise, the whole file has to be processed.  Right now, this is only 
	 * called once, for the initial file (often the Crazy steps).  Hopefully that
	 * will end up changing soon. */
	if( bKIUCompliant )
	{
		if( BPM2 > 0 && BPMPos2 > 0 )
		{
			HandleBunki( out.m_SongTiming, BPM1, BPM2, SMGap1, BPMPos2 );
		}

		if( BPM3 > 0 && BPMPos3 > 0 )
		{
			HandleBunki( out.m_SongTiming, BPM2, BPM3, SMGap2, BPMPos3 );
		}
	}
	else
	{
		float fCurBeat = 0.0f;
		bool bDMRequired = false;

		for( unsigned i=0; i < vNoteRows.size(); ++i )
		{
			RString& NoteRowString = vNoteRows[i];
			StripCrnl( NoteRowString );

			if( NoteRowString == "" )
				continue; // ignore empty rows.

			if( NoteRowString == "2222222222222" ) // Row of 2s = end. Confirm KIUCompliency here.
			{
				if (!bDMRequired)
					bKIUCompliant = true;
				break;
			}

			// This is where the DMRequired test will take place.
			if ( BeginsWith( NoteRowString, "|" ) )
			{
				// have a static timing for everything
				bDMRequired = true;
				continue;
			}
			else
			{
				// ignore whatever else...
				//continue;
			}
			
			fCurBeat += 1.0f / iTickCount;
		}
	}

	// Try to fill in missing bits of information from the pathname.
	{
		vector<RString> asBits;
		split( sPath, "/", asBits, true);

		ASSERT( asBits.size() > 1 );
		LoadTags( asBits[asBits.size()-2], out );
	}

	return true;
}
Beispiel #10
0
static bool LoadFromKSFFile( const RString &sPath, Steps &out, Song &song, bool bKIUCompliant )
{
	LOG->Trace( "Steps::LoadFromKSFFile( '%s' )", sPath.c_str() );

	MsdFile msd;
	if( !msd.ReadFile( sPath, false ) )  // don't unescape
	{
		LOG->UserLog( "Song file", sPath, "couldn't be opened: %s", msd.GetError().c_str() );
		return false;
	}

	// this is the value we read for TICKCOUNT
	int iTickCount = -1;
	// used to adapt weird tickcounts
	//float fScrollRatio = 1.0f; -- uncomment when ready to use.
	vector<RString> vNoteRows;

	// According to Aldo_MX, there is a default BPM and it's 60. -aj
	bool bDoublesChart = false;
	
	TimingData stepsTiming;
	float SMGap1 = 0, SMGap2 = 0, BPM1 = -1, BPMPos2 = -1, BPM2 = -1, BPMPos3 = -1, BPM3 = -1;

	for( unsigned i=0; i<msd.GetNumValues(); i++ )
	{
		const MsdFile::value_t &sParams = msd.GetValue( i );
		RString sValueName = sParams[0];
		sValueName.MakeUpper();

		/* handle the data...well, not this data: not related to steps.
		 * Skips INTRO, MUSICINTRO, TITLEFILE, DISCFILE, SONGFILE. */
		if (sValueName=="TITLE" || EndsWith(sValueName, "INTRO")
		    || EndsWith(sValueName, "FILE") )
		{

		}
		else if( sValueName=="BPM" )
		{
			BPM1 = StringToFloat(sParams[1]);
			stepsTiming.AddSegment( BPMSegment(0, BPM1) );
		}
		else if( sValueName=="BPM2" )
		{
			if (bKIUCompliant)
			{
				BPM2 = StringToFloat( sParams[1] );
			}
			else
			{
				// LOG an error.
			}
		}
		else if( sValueName=="BPM3" )
		{
			if (bKIUCompliant)
			{
				BPM3 = StringToFloat( sParams[1] );
			}
			else
			{
				// LOG an error.
			}
		}
		else if( sValueName=="BUNKI" )
		{
			if (bKIUCompliant)
			{
				BPMPos2 = StringToFloat( sParams[1] ) / 100.0f;
			}
			else
			{
				// LOG an error.
			}
		}
		else if( sValueName=="BUNKI2" )
		{
			if (bKIUCompliant)
			{
				BPMPos3 = StringToFloat( sParams[1] ) / 100.0f;
			}
			else
			{
				// LOG an error.
			}
		}
		else if( sValueName=="STARTTIME" )
		{
			SMGap1 = -StringToFloat( sParams[1] )/100;
			stepsTiming.m_fBeat0OffsetInSeconds = SMGap1;
		}
		// This is currently required for more accurate KIU BPM changes.  
		else if( sValueName=="STARTTIME2" )
		{
			if (bKIUCompliant)
			{
				SMGap2 = -StringToFloat( sParams[1] )/100;
			}
			else
			{
				// LOG an error.
			}
		}
		else if ( sValueName=="STARTTIME3" )
		{
			// STARTTIME3 only ensures this is a KIU compliant simfile.
			bKIUCompliant = true;
		}
		
		else if( sValueName=="TICKCOUNT" )
		{
			iTickCount = StringToInt( sParams[1] );
			if( iTickCount <= 0 )
			{
				LOG->UserLog( "Song file", sPath, "has an invalid tick count: %d.", iTickCount );
				return false;
			}
			stepsTiming.AddSegment( TickcountSegment(0, iTickCount));
		}
		
		else if( sValueName=="DIFFICULTY" )
		{
			out.SetMeter( max(StringToInt(sParams[1]), 1) );
		}
		// new cases from Aldo_MX's fork:
		else if( sValueName=="PLAYER" )
		{
			RString sPlayer = sParams[1];
			sPlayer.MakeLower();
			if( sPlayer.find( "double" ) != string::npos )
				bDoublesChart = true;
		}
		// This should always be last.
		else if( sValueName=="STEP" )
		{
			RString theSteps = sParams[1];
			TrimLeft( theSteps );
			split( theSteps, "\n", vNoteRows, true );
		}
	}

	if( iTickCount == -1 )
	{
		iTickCount = 4;
		LOG->UserLog( "Song file", sPath, "doesn't have a TICKCOUNT. Defaulting to %i.", iTickCount );
	}
	
	// Prepare BPM stuff already if the file uses KSF syntax.
	if( bKIUCompliant )
	{
		if( BPM2 > 0 && BPMPos2 > 0 )
		{
			HandleBunki( stepsTiming, BPM1, BPM2, SMGap1, BPMPos2 );
		}
		
		if( BPM3 > 0 && BPMPos3 > 0 )
		{
			HandleBunki( stepsTiming, BPM2, BPM3, SMGap2, BPMPos3 );
		}
	}

	NoteData notedata;	// read it into here

	{
		RString sDir, sFName, sExt;
		splitpath( sPath, sDir, sFName, sExt );
		sFName.MakeLower();

		out.SetDescription(sFName);
		// Check another before anything else... is this okay? -DaisuMaster
		if( sFName.find("another") != string::npos )
		{
			out.SetDifficulty( Difficulty_Edit );
			if( !out.GetMeter() ) out.SetMeter( 25 );
		}
		else if(sFName.find("wild") != string::npos || 
			sFName.find("wd") != string::npos || 
			sFName.find("crazy+") != string::npos || 
			sFName.find("cz+") != string::npos || 
			sFName.find("hardcore") != string::npos )
		{
			out.SetDifficulty( Difficulty_Challenge );
			if( !out.GetMeter() ) out.SetMeter( 20 );
		}
		else if(sFName.find("crazy") != string::npos || 
			sFName.find("cz") != string::npos || 
			sFName.find("nightmare") != string::npos || 
			sFName.find("nm") != string::npos || 
			sFName.find("crazydouble") != string::npos )
		{
			out.SetDifficulty( Difficulty_Hard );
			if( !out.GetMeter() ) out.SetMeter( 14 ); // Set the meters to the Pump scale, not DDR.
		}
		else if(sFName.find("hard") != string::npos || 
			sFName.find("hd") != string::npos || 
			sFName.find("freestyle") != string::npos || 
			sFName.find("fs") != string::npos || 
			sFName.find("double") != string::npos )
		{
			out.SetDifficulty( Difficulty_Medium );
			if( !out.GetMeter() ) out.SetMeter( 8 );
		}
		else if(sFName.find("easy") != string::npos || 
			sFName.find("ez") != string::npos || 
			sFName.find("normal") != string::npos )
		{
			// I wonder if I should leave easy fall into the Beginner difficulty... -DaisuMaster
			out.SetDifficulty( Difficulty_Easy );
			if( !out.GetMeter() ) out.SetMeter( 4 );
		}
		else if(sFName.find("beginner") != string::npos || 
			sFName.find("practice") != string::npos || sFName.find("pr") != string::npos  )
		{
			out.SetDifficulty( Difficulty_Beginner );
			if( !out.GetMeter() ) out.SetMeter( 4 );
		}
		else
		{
			out.SetDifficulty( Difficulty_Hard );
			if( !out.GetMeter() ) out.SetMeter( 10 );
		}

		out.m_StepsType = StepsType_pump_single;

		// Check for "halfdouble" before "double".
		if(sFName.find("halfdouble") != string::npos || 
		   sFName.find("half-double") != string::npos || 
		   sFName.find("h_double") != string::npos || 
		   sFName.find("hdb") != string::npos )
			out.m_StepsType = StepsType_pump_halfdouble;
		// Handle bDoublesChart from above as well. -aj
		else if(sFName.find("double") != string::npos || 
			sFName.find("nightmare") != string::npos || 
			sFName.find("freestyle") != string::npos || 
			sFName.find("db") != string::npos || 
			sFName.find("nm") != string::npos || 
			sFName.find("fs") != string::npos || bDoublesChart )
			out.m_StepsType = StepsType_pump_double;
		else if( sFName.find("_1") != string::npos )
			out.m_StepsType = StepsType_pump_single;
		else if( sFName.find("_2") != string::npos )
			out.m_StepsType = StepsType_pump_couple;
	}

	switch( out.m_StepsType )
	{
	case StepsType_pump_single: notedata.SetNumTracks( 5 ); break;
	case StepsType_pump_couple: notedata.SetNumTracks( 10 ); break;
	case StepsType_pump_double: notedata.SetNumTracks( 10 ); break;
	case StepsType_pump_routine: notedata.SetNumTracks( 10 ); break; // future files may have this?
	case StepsType_pump_halfdouble: notedata.SetNumTracks( 6 ); break;
	default: FAIL_M( ssprintf("%i", out.m_StepsType) );
	}

	int t = 0;
	int iHoldStartRow[13];
	for( t=0; t<13; t++ )
		iHoldStartRow[t] = -1;

	bool bTickChangeNeeded = false;
	int newTick = -1;
	float fCurBeat = 0.0f;
	float prevBeat = 0.0f; // Used for hold tails.

	for( unsigned r=0; r<vNoteRows.size(); r++ )
	{
		RString& sRowString = vNoteRows[r];
		StripCrnl( sRowString );

		if( sRowString == "" )
			continue;	// skip

		// All 2s indicates the end of the song.
		else if( sRowString == "2222222222222" )
		{
			// Finish any holds that didn't get...well, finished.
			for( t=0; t < notedata.GetNumTracks(); t++ )
			{
				if( iHoldStartRow[t] != -1 )	// this ends the hold
				{
					if( iHoldStartRow[t] == BeatToNoteRow(prevBeat) )
						notedata.SetTapNote( t, iHoldStartRow[t], TAP_ORIGINAL_TAP );
					else
						notedata.AddHoldNote(t,
								     iHoldStartRow[t],
								     BeatToNoteRow(prevBeat),
								     TAP_ORIGINAL_HOLD_HEAD );
				}
			}
			/* have this row be the last moment in the song, unless
			 * a future step ends later. */
			//float curTime = stepsTiming.GetElapsedTimeFromBeat(fCurBeat);
			//if (curTime > song.GetSpecifiedLastSecond())
			//{
			//	song.SetSpecifiedLastSecond(curTime);
			//}

			song.SetSpecifiedLastSecond( song.GetSpecifiedLastSecond() + 4 );

			break;
		}

		else if( BeginsWith(sRowString, "|") )
		{
			/*
			if (bKIUCompliant)
			{
				// Log an error, ignore the line.
				continue;
			}
			*/
			// gotta do something tricky here: if the bpm is below one then a couple of calculations
			// for scrollsegments will be made, example, bpm 0.2, tick 4000, the scrollsegment will
			// be 0. if the tickcount is non a stepmania standard then it will be adapted, a scroll
			// segment will then be added based on approximations. -DaisuMaster
			// eh better do it considering the tickcount (high tickcounts)

			// I'm making some experiments, please spare me...
			//continue;

			RString temp = sRowString.substr(2,sRowString.size()-3);
			float numTemp = StringToFloat(temp);
			if (BeginsWith(sRowString, "|T")) 
			{
				// duh
				iTickCount = static_cast<int>(numTemp);
				// I have been owned by the man -DaisuMaster
				stepsTiming.SetTickcountAtBeat( fCurBeat, clamp(iTickCount, 0, ROWS_PER_BEAT) );
			}
			else if (BeginsWith(sRowString, "|B")) 
			{
				// BPM
				stepsTiming.SetBPMAtBeat( fCurBeat, numTemp );
			}
			else if (BeginsWith(sRowString, "|E"))
			{
				// DelayBeat
				float fCurDelay = 60 / stepsTiming.GetBPMAtBeat(fCurBeat) * numTemp / iTickCount;
				fCurDelay += stepsTiming.GetDelayAtRow(BeatToNoteRow(fCurBeat) );
				stepsTiming.SetDelayAtBeat( fCurBeat, fCurDelay );
			}
			else if (BeginsWith(sRowString, "|D"))
			{
				// Delays
				float fCurDelay = stepsTiming.GetStopAtRow(BeatToNoteRow(fCurBeat) );
				fCurDelay += numTemp / 1000;
				stepsTiming.SetDelayAtBeat( fCurBeat, fCurDelay );
			}
			else if (BeginsWith(sRowString, "|M") || BeginsWith(sRowString, "|C"))
			{
				// multipliers/combo
				ComboSegment seg( BeatToNoteRow(fCurBeat), int(numTemp) );
				stepsTiming.AddSegment( seg );
			}
			else if (BeginsWith(sRowString, "|S"))
			{
				// speed segments
			}
			else if (BeginsWith(sRowString, "|F"))
			{
				// fakes
			}
			else if (BeginsWith(sRowString, "|X"))
			{
				// scroll segments
				ScrollSegment seg = ScrollSegment( BeatToNoteRow(fCurBeat), numTemp );
				stepsTiming.AddSegment( seg );
				//return true;
			}

			continue;
		}

		// Half-doubles is offset; "0011111100000".
		if( out.m_StepsType == StepsType_pump_halfdouble )
			sRowString.erase( 0, 2 );

		// Update TICKCOUNT for Direct Move files.
		if( bTickChangeNeeded )
		{
			iTickCount = newTick;
			bTickChangeNeeded = false;
		}

		for( t=0; t < notedata.GetNumTracks(); t++ )
		{
			if( sRowString[t] == '4' )
			{
				/* Remember when each hold starts; ignore the middle. */
				if( iHoldStartRow[t] == -1 )
					iHoldStartRow[t] = BeatToNoteRow(fCurBeat);
				continue;
			}

			if( iHoldStartRow[t] != -1 )	// this ends the hold
			{
				int iEndRow = BeatToNoteRow(prevBeat);
				if( iHoldStartRow[t] == iEndRow )
					notedata.SetTapNote( t, iHoldStartRow[t], TAP_ORIGINAL_TAP );
				else
				{
					//notedata.AddHoldNote( t, iHoldStartRow[t], iEndRow , TAP_ORIGINAL_PUMP_HEAD );
					notedata.AddHoldNote( t, iHoldStartRow[t], iEndRow , TAP_ORIGINAL_HOLD_HEAD );
				}
				iHoldStartRow[t] = -1;
			}

			TapNote tap;
			switch( sRowString[t] )
			{
			case '0':	tap = TAP_EMPTY;		break;
			case '1':	tap = TAP_ORIGINAL_TAP;		break;
				//allow setting more notetypes on ksf files, this may come in handy (it should) -DaisuMaster
			case 'M':
			case 'm':
						tap = TAP_ORIGINAL_MINE;
						break;
			case 'F':
			case 'f':
						tap = TAP_ORIGINAL_FAKE;
						break;
			case 'L':
			case 'l':
						tap = TAP_ORIGINAL_LIFT;
						break;
			default:
				LOG->UserLog( "Song file", sPath, "has an invalid row \"%s\"; corrupt notes ignored.",
					      sRowString.c_str() );
				//return false;
				tap = TAP_EMPTY;
				break;
			}

			notedata.SetTapNote(t, BeatToNoteRow(fCurBeat), tap);
		}
		prevBeat = fCurBeat;
		fCurBeat = prevBeat + 1.0f / iTickCount;
	}

	out.SetNoteData( notedata );
	out.m_Timing = stepsTiming;

	out.TidyUpData();

	out.SetSavedToDisk( true );	// we're loading from disk, so this is by definintion already saved

	return true;
}
Beispiel #11
0
  /*
  	if an entire formula  consists of negated operands,
  	replaces negative signes by positive signs and returns the new format
  	*/
  string RemoveUnnecessaryNegatives (const string& formula)
  {
  		
  		// TODO: Implement a system for conversion of negative products  		
  		if (!formula.length())
  			return formula;
  			
  		//if (strstr (formula, "*") || strstr (formula, "/"))
  		//	return formula;
  			
  		string result = "";
		list<string> ops = SeparateOperands (formula);

		if (!BeginsWith ("-", ops.front()))
			return formula;
			
		list<string>::const_iterator o = ops.begin();
		
		// initial orientation
		if (ops.front() != "-") 
		{
			result = StripB ("-", ops.front());
			o++;
		} /* if */

		for (; o != ops.end(); )
		{
			
			// skip multiplication and division
			if (*o == "*" || *o == "/" || *o == "^")
			{
				result +=  *o; 
				o++;
				
				if (o != ops.end()) 
				{
					result += *o; 
					o++;
				} /* if */				
			} /* if */	
			
					
			if (*o == "-")
			{
				// we should have a - sign at this point			
				o++; 
				if (o != ops.end())
				{
					result += " + " + *o;
					o++; 
				} /* if */
					
			} /* if */
			else if (BeginsWith ("-", *o))
			{
				result += " + " + StripB ("-", *o);			
				o++;
			} /*else if */
	
			else 
			{
				return formula;				
			} /* else */


		} /* for */

		result = StripB (" + ", result);
		result = StripB (" ", result);

		return result;
  } /* RemoveUnnecessaryNegatives */