Ejemplo n.º 1
0
FileType ActorUtil::GetFileType( const RString &sPath )
{
	RString sExt = GetExtension( sPath );
	sExt.MakeLower();
	
	if( sExt=="lua" )		return FT_Lua;
	else if( 
		sExt=="png" ||
		sExt=="jpg" || 
		sExt=="gif" || 
		sExt=="bmp" )		return FT_Bitmap;
	else if( 
		sExt=="ogg" ||
		sExt=="wav" || 
		sExt=="mp3" )		return FT_Sound;
	else if( 
		sExt=="avi" || 
		sExt=="mpeg" || 
		sExt=="mpg" )		return FT_Movie;
	else if( 
		sExt=="txt" )		return FT_Model;
	else if( sPath.size() > 0 && sPath[sPath.size()-1] == '/' )
					return FT_Directory;
	/* Do this last, to avoid the IsADirectory in most cases. */
	else if( IsADirectory(sPath)  )	return FT_Directory;
	else				return FileType_Invalid;
}
Ejemplo n.º 2
0
void NoteField::UncacheNoteSkin( const RString &sNoteSkin_ )
{
	RString sNoteSkinLower = sNoteSkin_;
	sNoteSkinLower.MakeLower();

	LOG->Trace("NoteField::CacheNoteSkin: release %s", sNoteSkinLower.c_str() );
	ASSERT_M( m_NoteDisplays.find(sNoteSkinLower) != m_NoteDisplays.end(), sNoteSkinLower );
	delete m_NoteDisplays[sNoteSkinLower];
	m_NoteDisplays.erase( sNoteSkinLower );
}
Ejemplo n.º 3
0
RageSurface *RageSurfaceUtils::LoadFile( const RString &sPath, RString &error, bool bHeaderOnly )
{
	{
		RageFile TestOpen;
		if( !TestOpen.Open( sPath ) )
		{
			error = TestOpen.GetError();
			return NULL;
		}
	}

	set<RString> FileTypes;
	vector<RString> const& exts= ActorUtil::GetTypeExtensionList(FT_Bitmap);
	for(vector<RString>::const_iterator curr= exts.begin();
			curr != exts.end(); ++curr)
	{
		FileTypes.insert(*curr);
	}

	RString format = GetExtension(sPath);
	format.MakeLower();

	bool bKeepTrying = true;

	/* If the extension matches a format, try that first. */
	if( FileTypes.find(format) != FileTypes.end() )
	{
	    RageSurface *ret = TryOpenFile( sPath, bHeaderOnly, error, format, bKeepTrying );
		if( ret )
			return ret;
		FileTypes.erase( format );
	}

	for( set<RString>::iterator it = FileTypes.begin(); bKeepTrying && it != FileTypes.end(); ++it )
	{
		RageSurface *ret = TryOpenFile( sPath, bHeaderOnly, error, *it, bKeepTrying );
		if( ret )
		{
			LOG->UserLog( "Graphic file", sPath, "is really %s", it->c_str() );
			return ret;
		}
	}

	return NULL;
}
Ejemplo n.º 4
0
void NoteField::CacheNoteSkin( const RString &sNoteSkin_ )
{
	RString sNoteSkinLower = sNoteSkin_;
	sNoteSkinLower.MakeLower();

	if( m_NoteDisplays.find(sNoteSkinLower) != m_NoteDisplays.end() )
		return;

	LockNoteSkin l( sNoteSkinLower );

	LOG->Trace("NoteField::CacheNoteSkin: cache %s", sNoteSkinLower.c_str() );
	NoteDisplayCols *nd = new NoteDisplayCols( GAMESTATE->GetCurrentStyle(m_pPlayerState->m_PlayerNumber)->m_iColsPerPlayer );

	for( int c=0; c<GAMESTATE->GetCurrentStyle(m_pPlayerState->m_PlayerNumber)->m_iColsPerPlayer; c++ )
		nd->display[c].Load( c, m_pPlayerState, m_fYReverseOffsetPixels );
	nd->m_ReceptorArrowRow.Load( m_pPlayerState, m_fYReverseOffsetPixels );
	nd->m_GhostArrowRow.Load( m_pPlayerState, m_fYReverseOffsetPixels );

	m_NoteDisplays[ sNoteSkinLower ] = nd;
}
Ejemplo n.º 5
0
FileType ActorUtil::GetFileType( const RString &sPath )
{
	RString sExt = GetExtension( sPath );
	sExt.MakeLower();

	etft_cont_t::iterator conversion_entry= ExtensionToFileType.find(sExt);
	if(conversion_entry != ExtensionToFileType.end())
	{
		return conversion_entry->second;
	}
	else if(sPath.size() > 0 && sPath[sPath.size()-1] == '/')
	{
		return FT_Directory;
	}
	/* Do this last, to avoid the IsADirectory in most cases. */
	else if(IsADirectory(sPath))
	{
		return FT_Directory;
	}
	return FileType_Invalid;
}
Ejemplo n.º 6
0
static void SearchForDifficulty( RString sTag, Steps *pOut )
{
	sTag.MakeLower();

	// Only match "Light" in parentheses.
	if( sTag.find( "(light" ) != sTag.npos )
	{
		pOut->SetDifficulty( Difficulty_Easy );
	}
	else if( sTag.find( "another" ) != sTag.npos )
	{
		pOut->SetDifficulty( Difficulty_Hard );
	}
	else if( sTag.find( "(solo)" ) != sTag.npos )
	{
		pOut->SetDescription( "Solo" );
		pOut->SetDifficulty( Difficulty_Edit );
	}

	LOG->Trace( "Tag \"%s\" is %s", sTag.c_str(), DifficultyToString(pOut->GetDifficulty()).c_str() );
}
Ejemplo n.º 7
0
bool Steps::GetNoteDataFromSimfile()
{
	// Replace the line below with the Steps' cache file.
	RString stepFile = this->GetFilename();
	RString extension = GetExtension(stepFile);
	extension.MakeLower(); // must do this because the code is expecting lowercase

	if (extension.empty() || extension == "ssc"
		|| extension == "ats") // remember cache files.
	{
		SSCLoader loader;
		if ( ! loader.LoadNoteDataFromSimfile(stepFile, *this) )
		{
			/*
			HACK: 7/20/12 -- see bugzilla #740
			users who edit songs using the ever popular .sm file
			that remove or tamper with the .ssc file later on 
			complain of blank steps in the editor after reloading.
			Despite the blank steps being well justified since 
			the cache files contain only the SSC step file,
			give the user some leeway and search for a .sm replacement
			*/
			SMLoader backup_loader;
			RString transformedStepFile = stepFile;
			transformedStepFile.Replace(".ssc", ".sm");
			
			return backup_loader.LoadNoteDataFromSimfile(transformedStepFile, *this);
		}
		else
		{
			return true;
		}
	}
	else if (extension == "sm")
	{
		SMLoader loader;
		return loader.LoadNoteDataFromSimfile(stepFile, *this);
	}
	else if (extension == "sma")
	{
		SMALoader loader;
		return loader.LoadNoteDataFromSimfile(stepFile, *this);
	}
	else if (extension == "dwi")
	{
		return DWILoader::LoadNoteDataFromSimfile(stepFile, *this);
	}
	else if (extension == "ksf")
	{
		return KSFLoader::LoadNoteDataFromSimfile(stepFile, *this);
	}
	else if (extension == "bms" || extension == "bml" || extension == "bme" || extension == "pms")
	{
		return BMSLoader::LoadNoteDataFromSimfile(stepFile, *this);
	}
	else if (extension == "edit")
	{
		// Try SSC, then fallback to SM.
		SSCLoader ldSSC;
		if(ldSSC.LoadNoteDataFromSimfile(stepFile, *this) != true)
		{
			SMLoader ldSM;
			return ldSM.LoadNoteDataFromSimfile(stepFile, *this);
		}
		else return true;
	}
	return false;
}
Ejemplo n.º 8
0
/*
 * Each dwMaxSize, dwTextureColorDepth and iAlphaBits are maximums; we may
 * use less.  iAlphaBits must be 0, 1 or 4.
 *
 * XXX: change iAlphaBits == 4 to iAlphaBits == 8 to indicate "as much alpha
 * as needed", since that's what it really is; still only use 4 in 16-bit textures.
 *
 * Dither forces dithering when loading 16-bit textures.
 * Stretch forces the loaded image to fill the texture completely.
 */
void RageBitmapTexture::Create()
{
	RageTextureID actualID = GetID();

	ASSERT( actualID.filename != "" );

	/* Load the image into a RageSurface. */
	RString error;
	RageSurface *pImg= NULL;
	if(actualID.filename == TEXTUREMAN->GetScreenTextureID().filename)
	{
		pImg= TEXTUREMAN->GetScreenSurface();
	}
	else
	{
		pImg= RageSurfaceUtils::LoadFile(actualID.filename, error);
	}

	/* Tolerate corrupt/unknown images. */
	if( pImg == NULL )
	{
		RString warning = ssprintf("RageBitmapTexture: Couldn't load %s: %s",
			actualID.filename.c_str(), error.c_str());
		LOG->Warn("%s", warning.c_str());
		Dialog::OK(warning, "missing_texture");
		pImg = RageSurfaceUtils::MakeDummySurface( 64, 64 );
		ASSERT( pImg != NULL );
	}

	if( actualID.bHotPinkColorKey )
		RageSurfaceUtils::ApplyHotPinkColorKey( pImg );

	{
		/* Do this after setting the color key for paletted images; it'll also return
		 * TRAIT_NO_TRANSPARENCY if the color key is never used. */
		int iTraits = RageSurfaceUtils::FindSurfaceTraits( pImg );
		if( iTraits & RageSurfaceUtils::TRAIT_NO_TRANSPARENCY )
			actualID.iAlphaBits = 0;
		else if( iTraits & RageSurfaceUtils::TRAIT_BOOL_TRANSPARENCY )
			actualID.iAlphaBits = 1;
	}

	// look in the file name for a format hints
	RString sHintString = GetID().filename + actualID.AdditionalTextureHints;
	sHintString.MakeLower();

	if( sHintString.find("32bpp") != string::npos )			actualID.iColorDepth = 32;
	else if( sHintString.find("16bpp") != string::npos )		actualID.iColorDepth = 16;
	if( sHintString.find("dither") != string::npos )		actualID.bDither = true;
	if( sHintString.find("stretch") != string::npos )		actualID.bStretch = true;
	if( sHintString.find("mipmaps") != string::npos )		actualID.bMipMaps = true;
	if( sHintString.find("nomipmaps") != string::npos )		actualID.bMipMaps = false;	// check for "nomipmaps" after "mipmaps"

	/* If the image is marked grayscale, then use all bits not used for alpha
	 * for the intensity.  This way, if an image has no alpha, you get an 8-bit
	 * grayscale; if it only has boolean transparency, you get a 7-bit grayscale. */
	if( sHintString.find("grayscale") != string::npos )		actualID.iGrayscaleBits = 8-actualID.iAlphaBits;

	/* This indicates that the only component in the texture is alpha; assume all
	 * color is white. */
	if( sHintString.find("alphamap") != string::npos )		actualID.iGrayscaleBits = 0;

	/* No iGrayscaleBits for images that are already paletted.  We don't support
	 * that; and that hint is intended for use on images that are already grayscale,
	 * it's not intended to change a color image into a grayscale image. */
	if( actualID.iGrayscaleBits != -1 && pImg->format->BitsPerPixel == 8 )
		actualID.iGrayscaleBits = -1;

	/* Cap the max texture size to the hardware max. */
	actualID.iMaxSize = min( actualID.iMaxSize, DISPLAY->GetMaxTextureSize() );

	/* Save information about the source. */
	m_iSourceWidth = pImg->w;
	m_iSourceHeight = pImg->h;

	/* in-game image dimensions are the same as the source graphic */
	m_iImageWidth = m_iSourceWidth;
	m_iImageHeight = m_iSourceHeight;

	/* if "doubleres" (high resolution) and we're not allowing high res textures, then image dimensions are half of the source */
	if( sHintString.find("doubleres") != string::npos )
	{
		if( !StepMania::GetHighResolutionTextures() )
		{
			m_iImageWidth = m_iImageWidth / 2;
			m_iImageHeight = m_iImageHeight / 2;
		}
	}

	/* image size cannot exceed max size */
	m_iImageWidth = min( m_iImageWidth, actualID.iMaxSize );
	m_iImageHeight = min( m_iImageHeight, actualID.iMaxSize );

	/* Texture dimensions need to be a power of two; jump to the next. */
	m_iTextureWidth = power_of_two(m_iImageWidth);
	m_iTextureHeight = power_of_two(m_iImageHeight);

	/* If we're under 8x8, increase it, to avoid filtering problems on odd hardware. */
	if( m_iTextureWidth < 8 || m_iTextureHeight < 8 )
	{
		actualID.bStretch = true;
		m_iTextureWidth = max( 8, m_iTextureWidth );
		m_iTextureHeight = max( 8, m_iTextureHeight );
	}

	ASSERT_M( m_iTextureWidth <= actualID.iMaxSize, ssprintf("w %i, %i", m_iTextureWidth, actualID.iMaxSize) );
	ASSERT_M( m_iTextureHeight <= actualID.iMaxSize, ssprintf("h %i, %i", m_iTextureHeight, actualID.iMaxSize) );

	if( actualID.bStretch )
	{
		/* The hints asked for the image to be stretched to the texture size,
		 * probably for tiling. */
		m_iImageWidth = m_iTextureWidth;
		m_iImageHeight = m_iTextureHeight;
	}

	if( pImg->w != m_iImageWidth || pImg->h != m_iImageHeight ) 
		RageSurfaceUtils::Zoom( pImg, m_iImageWidth, m_iImageHeight );

	if( actualID.iGrayscaleBits != -1 && DISPLAY->SupportsTextureFormat(RagePixelFormat_PAL) )
	{
		RageSurface *pGrayscale = RageSurfaceUtils::PalettizeToGrayscale( pImg, actualID.iGrayscaleBits, actualID.iAlphaBits );

		delete pImg;
		pImg = pGrayscale;
	}

	// Figure out which texture format we want the renderer to use.
	RagePixelFormat pixfmt;

	// If the source is palleted, always load as paletted if supported.
	if( pImg->format->BitsPerPixel == 8 && DISPLAY->SupportsTextureFormat(RagePixelFormat_PAL) )
	{
		pixfmt = RagePixelFormat_PAL;
	}
	else
	{
		// not paletted
		switch( actualID.iColorDepth )
		{
		case 16:
			{
				// Bits of alpha in the source:
				int iSourceAlphaBits = 8 - pImg->format->Loss[3];

				// Don't use more than we were hinted to.
				iSourceAlphaBits = min( actualID.iAlphaBits, iSourceAlphaBits );

				switch( iSourceAlphaBits )
				{
				case 0:
				case 1:
					pixfmt = RagePixelFormat_RGB5A1;
					break;
				default:
					pixfmt = RagePixelFormat_RGBA4;
					break;
				}
			}
			break;
		case 32:
			pixfmt = RagePixelFormat_RGBA8;
			break;
		default: FAIL_M( ssprintf("%i", actualID.iColorDepth) );
		}
	}

	// Make we're using a supported format. Every card supports either RGBA8 or RGBA4.
	if( !DISPLAY->SupportsTextureFormat(pixfmt) )
	{
		pixfmt = RagePixelFormat_RGBA8;
		if( !DISPLAY->SupportsTextureFormat(pixfmt) )
			pixfmt = RagePixelFormat_RGBA4;
	}

	/* Dither if appropriate.
	 * XXX: This is a special case: don't bother dithering to RGBA8888.
	 * We actually want to dither only if the destination has greater color depth
	 * on at least one color channel than the source. For example, it doesn't
	 * make sense to do this when pixfmt is RGBA5551 if the image is only RGBA555. */
	if( actualID.bDither && 
		(pixfmt==RagePixelFormat_RGBA4 || pixfmt==RagePixelFormat_RGB5A1) )
	{
		// Dither down to the destination format.
		const RageDisplay::RagePixelFormatDesc *pfd = DISPLAY->GetPixelFormatDesc(pixfmt);
		RageSurface *dst = CreateSurface( pImg->w, pImg->h, pfd->bpp,
			pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] );

		RageSurfaceUtils::ErrorDiffusionDither( pImg, dst );
		delete pImg;
		pImg = dst;
	}

	/* This needs to be done *after* the final resize, since that resize
	 * may introduce new alpha bits that need to be set.  It needs to be
	 * done *before* we set up the palette, since it might change it. */
	RageSurfaceUtils::FixHiddenAlpha( pImg );

	/* Scale up to the texture size, if needed. */
	RageSurfaceUtils::ConvertSurface( pImg, m_iTextureWidth, m_iTextureHeight,
		pImg->fmt.BitsPerPixel, pImg->fmt.Mask[0], pImg->fmt.Mask[1], pImg->fmt.Mask[2], pImg->fmt.Mask[3] );
	
	m_uTexHandle = DISPLAY->CreateTexture( pixfmt, pImg, actualID.bMipMaps );

	CreateFrameRects();


	{
		// Enforce frames in the image have even dimensions.
		// Otherwise, pixel/texel alignment will be off.
		int iDimensionMultiple = 2;

		if( sHintString.find("doubleres") != string::npos )
		{
			iDimensionMultiple = 4;
		}

		bool bRunCheck = true;

		// Don't check if the artist intentionally blanked the image by making it very tiny.
		if( this->GetSourceWidth()<=iDimensionMultiple || this->GetSourceHeight()<=iDimensionMultiple )
			bRunCheck = false;

		// HACK: Don't check song graphics. Many of them are weird dimensions.
		if( !TEXTUREMAN->GetOddDimensionWarning() )
			bRunCheck = false;

		// Don't check if this is the screen texture, the theme can't do anything
		// about it. -Kyz
		if(actualID == TEXTUREMAN->GetScreenTextureID())
		{
			bRunCheck= false;
		}

		if( bRunCheck  )
		{
			float fFrameWidth = this->GetSourceWidth() / (float)this->GetFramesWide();
			float fFrameHeight = this->GetSourceHeight() / (float)this->GetFramesHigh();
			float fBetterFrameWidth = ceilf(fFrameWidth/iDimensionMultiple) * iDimensionMultiple;
			float fBetterFrameHeight = ceilf(fFrameHeight/iDimensionMultiple) * iDimensionMultiple;
			float fBetterSourceWidth = this->GetFramesWide() * fBetterFrameWidth;
			float fBetterSourceHeight = this->GetFramesHigh() * fBetterFrameHeight;
			if( fFrameWidth!=fBetterFrameWidth || fFrameHeight!=fBetterFrameHeight )
			{
				RString sWarning = ssprintf(
					"The graphic '%s' has frame dimensions that aren't a multiple of %d.\n"
					"The entire image is %dx%d and frame size is %.1fx%.1f.\n"
					"Image quality will be much improved if you resize the graphic to %.0fx%.0f, which is a frame size of %.0fx%.0f.", 
					actualID.filename.c_str(), 
					iDimensionMultiple,
					this->GetSourceWidth(), this->GetSourceHeight(), 
					fFrameWidth, fFrameHeight,
					fBetterSourceWidth, fBetterSourceHeight,
					fBetterFrameWidth, fBetterFrameHeight );
				LOG->Warn( "%s", sWarning.c_str() );
				Dialog::OK( sWarning, "FRAME_DIMENSIONS_WARNING" );
			}
		}
	}


	delete pImg;

	// Check for hints that override the apparent "size".
	GetResolutionFromFileName( actualID.filename, m_iSourceWidth, m_iSourceHeight );

	/* if "doubleres" (high resolution) then we want the image to appear in-game
	 * with dimensions 1/2 of the source. So, cut down the source dimension here
	 * after everything above is finished operating with the real image
	 * source dimensions. */
	if( sHintString.find("doubleres") != string::npos )
	{
		m_iSourceWidth = m_iSourceWidth / 2;
		m_iSourceHeight = m_iSourceHeight / 2;
	}


	RString sProperties;
	sProperties += RagePixelFormatToString( pixfmt ) + " ";
	if( actualID.iAlphaBits == 0 ) sProperties += "opaque ";
	if( actualID.iAlphaBits == 1 ) sProperties += "matte ";
	if( actualID.bStretch ) sProperties += "stretch ";
	if( actualID.bDither ) sProperties += "dither ";
	sProperties.erase( sProperties.size()-1 );
	//LOG->Trace( "RageBitmapTexture: Loaded '%s' (%ux%u); %s, source %d,%d;  image %d,%d.",
	//	actualID.filename.c_str(), GetTextureWidth(), GetTextureHeight(),
	//	sProperties.c_str(), m_iSourceWidth, m_iSourceHeight,
	//	m_iImageWidth, m_iImageHeight );
}
Ejemplo n.º 9
0
bool PlayerOptions::FromOneModString( const RString &sOneMod, RString &sErrorOut )
{
	ASSERT_M( NOTESKIN != NULL, "The Noteskin Manager must be loaded in order to process mods." );

	RString sBit = sOneMod;
	sBit.MakeLower();
	Trim( sBit );

	/* "drunk"
	 * "no drunk"
	 * "150% drunk"
	 * "*2 100% drunk": approach at 2x normal speed */

	float level = 1;
	float speed = 1;
	vector<RString> asParts;
	split( sBit, " ", asParts, true );

	FOREACH_CONST( RString, asParts, s )
	{
		if( *s == "no" )
		{
			level = 0;
		}
		else if( isdigit((*s)[0]) || (*s)[0] == '-' )
		{
			/* If the last character is a *, they probably said "123*" when
			 * they meant "*123". */
			if( s->Right(1) == "*" )
			{
				// XXX: We know what they want, is there any reason not to handle it?
				// Yes. We should be strict in handling the format. -Chris
				sErrorOut = ssprintf("Invalid player options \"%s\"; did you mean '*%d'?", s->c_str(), StringToInt(*s) );
				return false;
			}
			else
			{
				level = StringToFloat( *s ) / 100.0f;
			}
		}
		else if( *s[0]=='*' )
		{
			sscanf( *s, "*%f", &speed );
			if( !isfinite(speed) )
				speed = 1.0f;
		}
	}

	sBit = asParts.back();

#define SET_FLOAT( opt ) { m_ ## opt = level; m_Speed ## opt = speed; }
	const bool on = (level > 0.5f);

	static Regex mult("^([0-9]+(\\.[0-9]+)?)x$");
	vector<RString> matches;
	if( mult.Compare(sBit, matches) )
	{
		StringConversion::FromString( matches[0], level );
		SET_FLOAT( fScrollSpeed )
		SET_FLOAT( fTimeSpacing )
		m_fTimeSpacing = 0;
		m_fMaxScrollBPM = 0;
	}
	else if( sscanf( sBit, "c%f", &level ) == 1 )
	{
		if( !isfinite(level) || level <= 0.0f )
			level = CMOD_DEFAULT;
		SET_FLOAT( fScrollBPM )
		SET_FLOAT( fTimeSpacing )
		m_fTimeSpacing = 1;
		m_fMaxScrollBPM = 0;
	}
	// oITG's m-mods
	else if( sscanf( sBit, "m%f", &level ) == 1 )
	{
		// OpenITG doesn't have this block:
		/*
		if( !isfinite(level) || level <= 0.0f )
			level = CMOD_DEFAULT;
		*/
		SET_FLOAT( fMaxScrollBPM )
		m_fTimeSpacing = 0;
	}

	else if( sBit == "clearall" )				Init();
	else if( sBit == "resetspeed" )
	{
		/* level is set to the values from Init() because all speed related
		   fields are being reset to initial values, and they each have different
		   initial values.  -kyz */
		level= 0;
		SET_FLOAT(fMaxScrollBPM);
		SET_FLOAT(fTimeSpacing);
		level= 1.0f;
		SET_FLOAT(fScrollSpeed);
		level= CMOD_DEFAULT;
		SET_FLOAT(fScrollBPM)
	}
Ejemplo n.º 10
0
void BGAnimationLayer::LoadFromAniLayerFile( const RString& sPath )
{
	/* Generic BGAs are new.  Animation directories with no INI are old and obsolete. 
	 * Don't combine them. */
	RString lcPath = sPath;
	lcPath.MakeLower();

	if( lcPath.find("usesongbg") != RString::npos )
	{
		const Song* pSong = GAMESTATE->m_pCurSong;
		RString sSongBGPath;
		if( pSong && pSong->HasBackground() )
			sSongBGPath = pSong->GetBackgroundPath();
		else
			sSongBGPath = THEME->GetPathG("Common","fallback background");

		Sprite* pSprite = new Sprite;
		pSprite->Load( Sprite::SongBGTexture(sSongBGPath) );
		pSprite->StretchTo( FullScreenRectF );
		this->AddChild( pSprite );

		return;		// this will ignore other effects in the file name
	}

	enum Effect {
		EFFECT_CENTER,
		EFFECT_STRETCH_STILL,
		EFFECT_STRETCH_SCROLL_LEFT,
		EFFECT_STRETCH_SCROLL_RIGHT,
		EFFECT_STRETCH_SCROLL_UP,
		EFFECT_STRETCH_SCROLL_DOWN,
		EFFECT_STRETCH_WATER,
		EFFECT_STRETCH_BUBBLE,
		EFFECT_STRETCH_TWIST,
		EFFECT_STRETCH_SPIN,
		EFFECT_PARTICLES_SPIRAL_OUT,
		EFFECT_PARTICLES_SPIRAL_IN,
		EFFECT_PARTICLES_FLOAT_UP,
		EFFECT_PARTICLES_FLOAT_DOWN,
		EFFECT_PARTICLES_FLOAT_LEFT,
		EFFECT_PARTICLES_FLOAT_RIGHT,
		EFFECT_PARTICLES_BOUNCE,
		EFFECT_TILE_STILL,
		EFFECT_TILE_SCROLL_LEFT,
		EFFECT_TILE_SCROLL_RIGHT,
		EFFECT_TILE_SCROLL_UP,
		EFFECT_TILE_SCROLL_DOWN,
		EFFECT_TILE_FLIP_X,
		EFFECT_TILE_FLIP_Y,
		EFFECT_TILE_PULSE,
		NUM_EFFECTS,		// leave this at the end
		EFFECT_INVALID
	};

	const RString EFFECT_STRING[NUM_EFFECTS] = {
		"center",
		"stretchstill",
		"stretchscrollleft",
		"stretchscrollright",
		"stretchscrollup",
		"stretchscrolldown",
		"stretchwater",
		"stretchbubble",
		"stretchtwist",
		"stretchspin",
		"particlesspiralout",
		"particlesspiralin",
		"particlesfloatup",
		"particlesfloatdown",
		"particlesfloatleft",
		"particlesfloatright",
		"particlesbounce",
		"tilestill",
		"tilescrollleft",
		"tilescrollright",
		"tilescrollup",
		"tilescrolldown",
		"tileflipx",
		"tileflipy",
		"tilepulse",
	};

	Effect effect = EFFECT_CENTER;

	for( int i=0; i<NUM_EFFECTS; i++ )
		if( lcPath.find(EFFECT_STRING[i]) != string::npos )
			effect = (Effect)i;

	switch( effect )
	{
	case EFFECT_CENTER:
		{
			m_Type = TYPE_SPRITE;
			Sprite* pSprite = new Sprite;
			this->AddChild( pSprite );
			pSprite->Load( sPath );
			pSprite->SetXY( SCREEN_CENTER_X, SCREEN_CENTER_Y );
		}
		break;
	case EFFECT_STRETCH_STILL:
	case EFFECT_STRETCH_SCROLL_LEFT:
	case EFFECT_STRETCH_SCROLL_RIGHT:
	case EFFECT_STRETCH_SCROLL_UP:
	case EFFECT_STRETCH_SCROLL_DOWN:
	case EFFECT_STRETCH_WATER:
	case EFFECT_STRETCH_BUBBLE:
	case EFFECT_STRETCH_TWIST:
		{
			m_Type = TYPE_SPRITE;
			Sprite* pSprite = new Sprite;
			this->AddChild( pSprite );
			RageTextureID ID(sPath);
			ID.bStretch = true;
			pSprite->Load( Sprite::SongBGTexture(ID) );
			pSprite->StretchTo( FullScreenRectF );
			pSprite->SetCustomTextureRect( RectF(0,0,1,1) );

			switch( effect )
			{
				case EFFECT_STRETCH_SCROLL_LEFT:	m_fTexCoordVelocityX = +0.5f; m_fTexCoordVelocityY = 0;	break;
				case EFFECT_STRETCH_SCROLL_RIGHT:	m_fTexCoordVelocityX = -0.5f; m_fTexCoordVelocityY = 0;	break;
				case EFFECT_STRETCH_SCROLL_UP:		m_fTexCoordVelocityX = 0; m_fTexCoordVelocityY = +0.5f;	break;
				case EFFECT_STRETCH_SCROLL_DOWN:	m_fTexCoordVelocityX = 0; m_fTexCoordVelocityY = -0.5f;	break;
				default: break;
			}
		}
		break;
	case EFFECT_STRETCH_SPIN:
		{
			m_Type = TYPE_SPRITE;
			Sprite* pSprite = new Sprite;
			this->AddChild( pSprite );
			pSprite->Load( Sprite::SongBGTexture(sPath) );
			const RectF StretchedFullScreenRectF(
				FullScreenRectF.left-200,
				FullScreenRectF.top-200,
				FullScreenRectF.right+200,
				FullScreenRectF.bottom+200 );

			pSprite->ScaleToCover( StretchedFullScreenRectF );
			pSprite->SetEffectSpin( RageVector3(0,0,60) );
		}
		break;
	case EFFECT_PARTICLES_SPIRAL_OUT:
	case EFFECT_PARTICLES_SPIRAL_IN:
	case EFFECT_PARTICLES_FLOAT_UP:
	case EFFECT_PARTICLES_FLOAT_DOWN:
	case EFFECT_PARTICLES_FLOAT_LEFT:
	case EFFECT_PARTICLES_FLOAT_RIGHT:
	case EFFECT_PARTICLES_BOUNCE:
		{
			m_Type = TYPE_PARTICLES;
			Sprite s;
			s.Load( sPath );
			int iSpriteArea = int( s.GetUnzoomedWidth()*s.GetUnzoomedHeight() );
			const int iMaxArea = int(SCREEN_WIDTH*SCREEN_HEIGHT);
			int iNumParticles = iMaxArea / iSpriteArea;
			iNumParticles = min( iNumParticles, MAX_SPRITES );

			for( int i=0; i<iNumParticles; i++ )
			{
				Sprite* pSprite = new Sprite;
				this->AddChild( pSprite );
				pSprite->Load( sPath );
				pSprite->SetZoom( 0.7f + 0.6f*i/(float)iNumParticles );
				switch( effect )
				{
				case EFFECT_PARTICLES_BOUNCE:
					pSprite->SetX( randomf( GetGuardRailLeft(pSprite), GetGuardRailRight(pSprite) ) );
					pSprite->SetY( randomf( GetGuardRailTop(pSprite), GetGuardRailBottom(pSprite) ) );
					break;
				default:
					pSprite->SetX( randomf( GetOffScreenLeft(pSprite), GetOffScreenRight(pSprite) ) );
					pSprite->SetY( randomf( GetOffScreenTop(pSprite), GetOffScreenBottom(pSprite) ) );
					break;
				}

				switch( effect )
				{
				case EFFECT_PARTICLES_FLOAT_UP:
				case EFFECT_PARTICLES_SPIRAL_OUT:
					m_vParticleVelocity.push_back( RageVector3( 0, -PARTICLE_SPEED*pSprite->GetZoom(), 0 ) );
					break;
				case EFFECT_PARTICLES_FLOAT_DOWN:
				case EFFECT_PARTICLES_SPIRAL_IN:
					m_vParticleVelocity.push_back( RageVector3( 0, PARTICLE_SPEED*pSprite->GetZoom(), 0 ) );
					break;
				case EFFECT_PARTICLES_FLOAT_LEFT:
					m_vParticleVelocity.push_back( RageVector3( -PARTICLE_SPEED*pSprite->GetZoom(), 0, 0 ) );
					break;
				case EFFECT_PARTICLES_FLOAT_RIGHT:
					m_vParticleVelocity.push_back( RageVector3( +PARTICLE_SPEED*pSprite->GetZoom(), 0, 0 ) );
					break;
				case EFFECT_PARTICLES_BOUNCE:
					m_bParticlesBounce = true;
					pSprite->SetZoom( 1 );
					m_vParticleVelocity.push_back( RageVector3( randomf(), randomf(), 0 ) );
					RageVec3Normalize( &m_vParticleVelocity[i], &m_vParticleVelocity[i] );
					m_vParticleVelocity[i].x *= PARTICLE_SPEED;
					m_vParticleVelocity[i].y *= PARTICLE_SPEED;
					break;
				default:
					FAIL_M(ssprintf("Unrecognized layer effect: %i", effect));
				}
			}
		}
		break;
	case EFFECT_TILE_STILL:
	case EFFECT_TILE_SCROLL_LEFT:
	case EFFECT_TILE_SCROLL_RIGHT:
	case EFFECT_TILE_SCROLL_UP:
	case EFFECT_TILE_SCROLL_DOWN:
	case EFFECT_TILE_FLIP_X:
	case EFFECT_TILE_FLIP_Y:
	case EFFECT_TILE_PULSE:
		{
			m_Type = TYPE_TILES;
			RageTextureID ID(sPath);
			ID.bStretch = true;
			Sprite s;
			s.Load( ID );
			m_iNumTilesWide = 2+int(SCREEN_WIDTH /s.GetUnzoomedWidth());
			m_iNumTilesWide = min( m_iNumTilesWide, MAX_TILES_WIDE );
			m_iNumTilesHigh = 2+int(SCREEN_HEIGHT/s.GetUnzoomedHeight());
			m_iNumTilesHigh = min( m_iNumTilesHigh, MAX_TILES_HIGH );
			m_fTilesStartX = s.GetUnzoomedWidth() / 2;
			m_fTilesStartY = s.GetUnzoomedHeight() / 2;
			m_fTilesSpacingX = s.GetUnzoomedWidth();
			m_fTilesSpacingY = s.GetUnzoomedHeight();
			for( int x=0; x<m_iNumTilesWide; x++ )
			{
				for( int y=0; y<m_iNumTilesHigh; y++ )
				{
					Sprite* pSprite = new Sprite;
					this->AddChild( pSprite );
					pSprite->Load( ID );
					pSprite->SetTextureWrapping( true );	// gets rid of some "cracks"

					switch( effect )
					{
					case EFFECT_TILE_STILL:
						break;
					case EFFECT_TILE_SCROLL_LEFT:
						m_fTileVelocityX = -PARTICLE_SPEED;
						break;
					case EFFECT_TILE_SCROLL_RIGHT:
						m_fTileVelocityX = +PARTICLE_SPEED;
						break;
					case EFFECT_TILE_SCROLL_UP:
						m_fTileVelocityY = -PARTICLE_SPEED;
						break;
					case EFFECT_TILE_SCROLL_DOWN:
						m_fTileVelocityY = +PARTICLE_SPEED;
						break;
					case EFFECT_TILE_FLIP_X:
						pSprite->SetEffectSpin( RageVector3(180,0,0) );
						break;
					case EFFECT_TILE_FLIP_Y:
						pSprite->SetEffectSpin( RageVector3(0,180,0) );
						break;
					case EFFECT_TILE_PULSE:
						pSprite->SetEffectPulse( 1, 0.3f, 1.f );
						break;
					default:
						FAIL_M(ssprintf("Not a tile effect: %i", effect));
					}
				}
			}
		}
		break;
	default:
		FAIL_M(ssprintf("Unrecognized layer effect: %i", effect));
	}


	RString sHint = sPath;
	sHint.MakeLower();

	if( sHint.find("cyclecolor") != RString::npos )
		for( unsigned i=0; i<m_SubActors.size(); i++ )
			m_SubActors[i]->SetEffectRainbow( 5 );

	if( sHint.find("cyclealpha") != RString::npos )
		for( unsigned i=0; i<m_SubActors.size(); i++ )
			m_SubActors[i]->SetEffectDiffuseShift( 2, RageColor(1,1,1,1), RageColor(1,1,1,0) );

	if( sHint.find("startonrandomframe") != RString::npos )
		for( unsigned i=0; i<m_SubActors.size(); i++ )
			m_SubActors[i]->SetState( RandomInt(m_SubActors[i]->GetNumStates()) );

	if( sHint.find("dontanimate") != RString::npos )
		for( unsigned i=0; i<m_SubActors.size(); i++ )
			m_SubActors[i]->StopAnimating();

	if( sHint.find("add") != RString::npos )
		for( unsigned i=0; i<m_SubActors.size(); i++ )
			m_SubActors[i]->SetBlendMode( BLEND_ADD );
}
Ejemplo n.º 11
0
void BGAnimationLayer::LoadFromNode( const XNode* pNode )
{
	{
		bool bCond;
		if( pNode->GetAttrValue("Condition", bCond) && !bCond )
			return;
	}

	bool bStretch = false;
	{
		RString type = "sprite";
		pNode->GetAttrValue( "Type", type );
		type.MakeLower();

		/* The preferred way of stretching a sprite to fit the screen is "Type=sprite"
		 * and "stretch=1".  "type=1" is for backwards-compatibility. */
		pNode->GetAttrValue( "Stretch", bStretch );

		// Check for string match first, then do integer match.
		// "if(StringType(type)==0)" was matching against all string matches.
		// -Chris
		if( type.EqualsNoCase("sprite") )
		{
			m_Type = TYPE_SPRITE;
		}
		else if( type.EqualsNoCase("particles") )
		{
			m_Type = TYPE_PARTICLES;
		}
		else if( type.EqualsNoCase("tiles") )
		{
			m_Type = TYPE_TILES;
		}
		else if( StringToInt(type) == 1 )
		{
			m_Type = TYPE_SPRITE; 
			bStretch = true; 
		}
		else if( StringToInt(type) == 2 )
		{
			m_Type = TYPE_PARTICLES; 
		}
		else if( StringToInt(type) == 3 )
		{
			m_Type = TYPE_TILES; 
		}
		else
		{
			m_Type = TYPE_SPRITE;
		}
	}

	pNode->GetAttrValue( "FOV", m_fFOV );
	pNode->GetAttrValue( "Lighting", m_bLighting );

	pNode->GetAttrValue( "TexCoordVelocityX", m_fTexCoordVelocityX );
	pNode->GetAttrValue( "TexCoordVelocityY", m_fTexCoordVelocityY );

	// compat:
	pNode->GetAttrValue( "StretchTexCoordVelocityX", m_fTexCoordVelocityX );
	pNode->GetAttrValue( "StretchTexCoordVelocityY", m_fTexCoordVelocityY );

	// particle and tile stuff
	float fZoomMin = 1;
	float fZoomMax = 1;
	pNode->GetAttrValue( "ZoomMin", fZoomMin );
	pNode->GetAttrValue( "ZoomMax", fZoomMax );

	float fVelocityXMin = 10, fVelocityXMax = 10;
	float fVelocityYMin = 0, fVelocityYMax = 0;
	float fVelocityZMin = 0, fVelocityZMax = 0;
	float fOverrideSpeed = 0;		// 0 means don't override speed
	pNode->GetAttrValue( "VelocityXMin", fVelocityXMin );
	pNode->GetAttrValue( "VelocityXMax", fVelocityXMax );
	pNode->GetAttrValue( "VelocityYMin", fVelocityYMin );
	pNode->GetAttrValue( "VelocityYMax", fVelocityYMax );
	pNode->GetAttrValue( "VelocityZMin", fVelocityZMin );
	pNode->GetAttrValue( "VelocityZMax", fVelocityZMax );
	pNode->GetAttrValue( "OverrideSpeed", fOverrideSpeed );

	int iNumParticles = 10;
	pNode->GetAttrValue( "NumParticles", iNumParticles );

	pNode->GetAttrValue( "ParticlesBounce", m_bParticlesBounce );
	pNode->GetAttrValue( "TilesStartX", m_fTilesStartX );
	pNode->GetAttrValue( "TilesStartY", m_fTilesStartY );
	pNode->GetAttrValue( "TilesSpacingX", m_fTilesSpacingX );
	pNode->GetAttrValue( "TilesSpacingY", m_fTilesSpacingY );
	pNode->GetAttrValue( "TileVelocityX", m_fTileVelocityX );
	pNode->GetAttrValue( "TileVelocityY", m_fTileVelocityY );


	switch( m_Type )
	{
	case TYPE_SPRITE:
		{
			Actor* pActor = ActorUtil::LoadFromNode( pNode, this );
			this->AddChild( pActor );
			if( bStretch )
				pActor->StretchTo( FullScreenRectF );
		}
		break;
	case TYPE_PARTICLES:
		{
			RString sFile;
			ActorUtil::GetAttrPath( pNode, "File", sFile );
			FixSlashesInPlace( sFile );

			CollapsePath( sFile );

			for( int i=0; i<iNumParticles; i++ )
			{
				Actor* pActor = ActorUtil::MakeActor( sFile, this );
				if( pActor == NULL )
					continue;
				this->AddChild( pActor );
				pActor->SetXY( randomf(float(FullScreenRectF.left),float(FullScreenRectF.right)),
							   randomf(float(FullScreenRectF.top),float(FullScreenRectF.bottom)) );
				pActor->SetZoom( randomf(fZoomMin,fZoomMax) );
				m_vParticleVelocity.push_back( RageVector3( 
					randomf(fVelocityXMin,fVelocityXMax),
					randomf(fVelocityYMin,fVelocityYMax),
					randomf(fVelocityZMin,fVelocityZMax) ) );
				if( fOverrideSpeed != 0 )
				{
					RageVec3Normalize( &m_vParticleVelocity[i], &m_vParticleVelocity[i] );
					m_vParticleVelocity[i] *= fOverrideSpeed;
				}
			}
		}
		break;
	case TYPE_TILES:
		{
			RString sFile;
			ActorUtil::GetAttrPath( pNode, "File", sFile );
			FixSlashesInPlace( sFile );

			CollapsePath( sFile );

			AutoActor s;
			s.Load( sFile );
			if( m_fTilesSpacingX == -1 )
				m_fTilesSpacingX = s->GetUnzoomedWidth();
			if( m_fTilesSpacingY == -1 )
				m_fTilesSpacingY = s->GetUnzoomedHeight();
			m_iNumTilesWide = 2+(int)(SCREEN_WIDTH /m_fTilesSpacingX);
			m_iNumTilesHigh = 2+(int)(SCREEN_HEIGHT/m_fTilesSpacingY);
			unsigned NumSprites = m_iNumTilesWide * m_iNumTilesHigh;
			for( unsigned i=0; i<NumSprites; i++ )
			{
				Actor* pSprite = ActorUtil::MakeActor( sFile, this );
				if( pSprite == NULL )
					continue;
				this->AddChild( pSprite );
				pSprite->SetTextureWrapping( true );		// gets rid of some "cracks"
				pSprite->SetZoom( randomf(fZoomMin,fZoomMax) );
			}
		}
		break;
	default:
		FAIL_M(ssprintf("Unrecognized layer type: %i", m_Type));
	}

	bool bStartOnRandomFrame = false;
	pNode->GetAttrValue( "StartOnRandomFrame", bStartOnRandomFrame );
	if( bStartOnRandomFrame )
	{
		for( unsigned i=0; i<m_SubActors.size(); i++ )
			m_SubActors[i]->SetState( RandomInt(m_SubActors[i]->GetNumStates()) );
	}
}
Ejemplo n.º 12
0
void ScreenJukebox::SetSong()
{
	ThemeMetric<bool>	ALLOW_ADVANCED_MODIFIERS(m_sName,"AllowAdvancedModifiers");

	vector<Song*> vSongs;

	/* Check to see if there is a theme course. If there is a course that has
	 * the exact same name as the theme, then we pick a song from this course. */
	Course *pCourse = SONGMAN->GetCourseFromName( THEME->GetCurThemeName() );
	if( pCourse != NULL )
		for ( unsigned i = 0; i < pCourse->m_vEntries.size(); i++ )
			if( pCourse->m_vEntries[i].IsFixedSong() )
				vSongs.push_back( pCourse->m_vEntries[i].songID.ToSong() );

	if ( vSongs.size() == 0 )
		vSongs = SONGMAN->GetSongs( GAMESTATE->m_sPreferredSongGroup );
	// Still nothing?
	if( vSongs.size() == 0 )
		return;


	// Calculate what difficulties to show
	vector<Difficulty> vDifficultiesToShow;
	if( m_bDemonstration )
	{
		// HACK: This belongs in ScreenDemonstration.
		ThemeMetricDifficultiesToShow	DIFFICULTIES_TO_SHOW_HERE(m_sName,"DifficultiesToShow");
		vDifficultiesToShow = DIFFICULTIES_TO_SHOW_HERE.GetValue();
	}
	else
	{
		if( GAMESTATE->m_PreferredDifficulty[PLAYER_1] != Difficulty_Invalid )
		{
			vDifficultiesToShow.push_back( GAMESTATE->m_PreferredDifficulty[PLAYER_1] );
		}
		else
		{
			FOREACH_ENUM( Difficulty, dc )
				vDifficultiesToShow.push_back( dc );
		}
	}

	ASSERT( !vDifficultiesToShow.empty() );

	// Search for a Song and Steps to play during the demo.
	for( int i=0; i<1000; i++ )
	{
		Song* pSong = vSongs[RandomInt(vSongs.size())];

		ASSERT( pSong != NULL );
		if( !pSong->HasMusic() )
			continue;	// skip
		if( !pSong->NormallyDisplayed() )
			continue;
		if( !pSong->ShowInDemonstrationAndRanking() )
			continue;	// skip

		Difficulty dc = vDifficultiesToShow[ RandomInt(vDifficultiesToShow.size()) ];
		Steps* pSteps = SongUtil::GetStepsByDifficulty( pSong, GAMESTATE->GetCurrentStyle()->m_StepsType, dc );

		if( pSteps == NULL )
			continue;	// skip

		if( !PREFSMAN->m_bAutogenSteps && pSteps->IsAutogen())
			continue;	// skip

		// Found something we can use!
		GAMESTATE->m_pCurSong.Set( pSong );
		// We just changed the song. Reset the original sync data.
		AdjustSync::ResetOriginalSyncData();
		FOREACH_PlayerNumber( p )
			GAMESTATE->m_pCurSteps[p].Set( pSteps );

		bool bShowModifiers = randomf(0,1) <= SHOW_COURSE_MODIFIERS_PROBABILITY;
		if( bShowModifiers )
		{
			/* If we have a modifier course containing this song, apply its
			 * modifiers. Only check fixed course entries. */
			vector<Course*> apCourses;
			SONGMAN->GetAllCourses( apCourses, false );
			vector<const CourseEntry *> apOptions;
			vector<Course*> apPossibleCourses;
			for( unsigned j = 0; j < apCourses.size(); ++j )
			{
				Course *lCourse = apCourses[j];
				const CourseEntry *pEntry = lCourse->FindFixedSong( pSong );
				if( pEntry == NULL || pEntry->attacks.size() == 0 )
					continue;

				if( !ALLOW_ADVANCED_MODIFIERS )
				{
					// There are some confusing mods that we don't want to show in demonstration.
					bool bModsAreOkToShow = true;
					AttackArray aAttacks = pEntry->attacks;
					if( !pEntry->sModifiers.empty() )
						aAttacks.push_back( Attack::FromGlobalCourseModifier( pEntry->sModifiers ) );
					FOREACH_CONST( Attack, aAttacks, a )
					{
						RString s = a->sModifiers;
						s.MakeLower();
						// todo: allow themers to modify this list? -aj
						if( s.find("dark") != string::npos ||
							s.find("stealth") != string::npos )
						{
							bModsAreOkToShow = false;
							break;
						}
					}
					if( !bModsAreOkToShow )
						continue;	// skip
				}

				apOptions.push_back( pEntry );
				apPossibleCourses.push_back( pCourse );
			}

			if( !apOptions.empty() )
			{
				int iIndex = RandomInt( apOptions.size() );
				m_pCourseEntry = apOptions[iIndex];
				Course *lCourse = apPossibleCourses[iIndex]; 

				PlayMode pm = CourseTypeToPlayMode( lCourse->GetCourseType() );
				GAMESTATE->m_PlayMode.Set( pm );
				GAMESTATE->m_pCurCourse.Set( lCourse );
				FOREACH_PlayerNumber( p )
				{
					GAMESTATE->m_pCurTrail[p].Set( lCourse->GetTrail( GAMESTATE->GetCurrentStyle()->m_StepsType ) );
					ASSERT( GAMESTATE->m_pCurTrail[p] != NULL );
				}
			}
Ejemplo n.º 13
0
int CheckEnum( lua_State *L, LuaReference &table, int iPos, int iInvalid, const char *szType, bool bAllowInvalid )
{
	luaL_checkany( L, iPos );

	if( lua_isnil(L, iPos) )
	{
		if( bAllowInvalid )
			return iInvalid;

		LuaHelpers::Push( L, ssprintf("Expected %s; got nil", szType) );
		lua_error( L );
	}

	iPos = LuaHelpers::AbsIndex( L, iPos );

	table.PushSelf( L );
	lua_pushvalue( L, iPos );
	lua_gettable( L, -2 );

	// If not found, check case-insensitively for legacy compatibility
	if( lua_isnil(L, -1) && lua_isstring(L, iPos) ) {
		RString sLower;

		// Get rid of nil value on stack
		lua_pop( L, 1 );

		// Get the string and lowercase it
		lua_pushvalue( L, iPos );
		LuaHelpers::Pop( L, sLower );
		sLower.MakeLower();

		// Try again to read the value
		table.PushSelf( L );
		LuaHelpers::Push( L, sLower );
		lua_gettable( L, -2 );
	}

	// If the result is nil, then a string was passed that is not a member of this enum.  Throw
	// an error.  To specify the invalid value, pass nil.  That way, typos will throw an error,
	// and not silently result in nil, or an out-of-bounds value.
	if( unlikely(lua_isnil(L, -1)) )
	{
		RString sGot;
		if( lua_isstring(L, iPos) )
		{
			/* We were given a string, but it wasn't a valid value for this enum.  Show
			 * the string. */
			lua_pushvalue( L, iPos );
			LuaHelpers::Pop( L, sGot );
			sGot = ssprintf( "\"%s\"", sGot.c_str() );
		}
		else
		{
			/* We didn't get a string.  Show the type. */
			luaL_pushtype( L, iPos );
			LuaHelpers::Pop( L, sGot );
		}
		LuaHelpers::Push( L, ssprintf("Expected %s; got %s", szType, sGot.c_str() ) );
		lua_error( L );
	}
	int iRet = lua_tointeger( L, -1 );
	lua_pop( L, 2 );
	return iRet;
}
Ejemplo n.º 14
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;
}
Ejemplo n.º 15
0
static void InitCharAliases()
{
	if(!CharAliases.empty())
		return;

	CharAliases["default"]		= FONT_DEFAULT_GLYPH;	/* ? */
	CharAliases["invalid"]		= INVALID_CHAR;			/* 0xFFFD */

	/* The comments here are UTF-8; they won't show up in VC6 (use your
	 * imagination). */

#define INTERNAL 0xE000

	/* Hiragana: */
	struct alias {
		const char *str;
		wchar_t chr;
	} aliases[] = {
		{ "ha", 	0x3042 }, /* あ */
		{ "hi",		0x3044 }, /* い */
		{ "hu",		0x3046 }, /* う */
		{ "he",		0x3048 }, /* え */
		{ "ho",		0x304a }, /* お */
		{ "hka",	0x304b }, /* か */
		{ "hki",	0x304d }, /* き */
		{ "hku",	0x304f }, /* く */
		{ "hke",	0x3051 }, /* け */
		{ "hko",	0x3053 }, /* こ */
		{ "hga",	0x304c }, /* が */
		{ "hgi",	0x304e }, /* ぎ */
		{ "hgu",	0x3050 }, /* ぐ */
		{ "hge",	0x3052 }, /* げ */
		{ "hgo",	0x3054 }, /* ご */
		{ "hza",	0x3056 }, /* ざ */
		{ "hzi",	0x3058 }, /* じ */
		{ "hzu",	0x305a }, /* ず */
		{ "hze",	0x305c }, /* ぜ */
		{ "hzo",	0x305e }, /* ぞ */
		{ "hta",	0x305f }, /* た */
		{ "hti",	0x3061 }, /* ち */
		{ "htu",	0x3064 }, /* つ */
		{ "hte",	0x3066 }, /* て */
		{ "hto",	0x3068 }, /* と */
		{ "hda",	0x3060 }, /* だ */
		{ "hdi",	0x3062 }, /* ぢ */
		{ "hdu",	0x3065 }, /* づ */
		{ "hde",	0x3067 }, /* で */
		{ "hdo",	0x3069 }, /* ど */
		{ "hna",	0x306a }, /* な */
		{ "hni",	0x306b }, /* に */
		{ "hnu",	0x306c }, /* ぬ */
		{ "hne",	0x306d }, /* ね */
		{ "hno",	0x306e }, /* の */
		{ "hha",	0x306f }, /* は */
		{ "hhi",	0x3072 }, /* ひ */
		{ "hhu",	0x3075 }, /* ふ */
		{ "hhe",	0x3078 }, /* へ */
		{ "hho",	0x307b }, /* ほ */
		{ "hba",	0x3070 }, /* ば */
		{ "hbi",	0x3073 }, /* び */
		{ "hbu",	0x3076 }, /* ぶ */
		{ "hbe",	0x3079 }, /* べ */
		{ "hbo",	0x307c }, /* ぼ */
		{ "hpa",	0x3071 }, /* ぱ */
		{ "hpi",	0x3074 }, /* ぴ */
		{ "hpu",	0x3077 }, /* ぷ */
		{ "hpe",	0x307a }, /* ぺ */
		{ "hpo",	0x307d }, /* ぽ */
		{ "hma",	0x307e }, /* ま */
		{ "hmi",	0x307f }, /* み */
		{ "hmu",	0x3080 }, /* む */
		{ "hme",	0x3081 }, /* め */
		{ "hmo",	0x3082 }, /* も */
		{ "hya",	0x3084 }, /* や */
		{ "hyu",	0x3086 }, /* ゆ */
		{ "hyo",	0x3088 }, /* よ */
		{ "hra",	0x3089 }, /* ら */
		{ "hri",	0x308a }, /* り */
		{ "hru",	0x308b }, /* る */
		{ "hre",	0x308c }, /* れ */
		{ "hro",	0x308d }, /* ろ */
		{ "hwa",	0x308f }, /* わ */
		{ "hwi",	0x3090 }, /* ゐ */
		{ "hwe",	0x3091 }, /* ゑ */
		{ "hwo",	0x3092 }, /* を */
		{ "hn",		0x3093 }, /* ん */
		{ "hvu",	0x3094 }, /* ゔ */
		{ "has",	0x3041 }, /* ぁ */
		{ "his",	0x3043 }, /* ぃ */
		{ "hus",	0x3045 }, /* ぅ */
		{ "hes",	0x3047 }, /* ぇ */
		{ "hos",	0x3049 }, /* ぉ */
		{ "hkas",	0x3095 }, /* ゕ */
		{ "hkes",	0x3096 }, /* ゖ */
		{ "hsa",	0x3055 }, /* さ */
		{ "hsi",	0x3057 }, /* し */
		{ "hsu",	0x3059 }, /* す */
		{ "hse",	0x305b }, /* せ */
		{ "hso",	0x305d }, /* そ */
		{ "hyas",	0x3083 }, /* ゃ */
		{ "hyus",	0x3085 }, /* ゅ */
		{ "hyos",	0x3087 }, /* ょ */
		{ "hwas",	0x308e }, /* ゎ */

		/* Katakana: */
		{ "hq",		0x3063 }, /* っ */
		{ "ka",		0x30a2 }, /* ア */
		{ "ki",		0x30a4 }, /* イ */
		{ "ku",		0x30a6 }, /* ウ */
		{ "ke",		0x30a8 }, /* エ */
		{ "ko",		0x30aa }, /* オ */
		{ "kka",	0x30ab }, /* カ */
		{ "kki",	0x30ad }, /* キ */
		{ "kku",	0x30af }, /* ク */
		{ "kke",	0x30b1 }, /* ケ */
		{ "kko",	0x30b3 }, /* コ */
		{ "kga",	0x30ac }, /* ガ */
		{ "kgi",	0x30ae }, /* ギ */
		{ "kgu",	0x30b0 }, /* グ */
		{ "kge",	0x30b2 }, /* ゲ */
		{ "kgo",	0x30b4 }, /* ゴ */
		{ "kza",	0x30b6 }, /* ザ */
		{ "kzi",	0x30b8 }, /* ジ */
		{ "kji",	0x30b8 }, /* ジ */ /* zi/ji alias */
		{ "kzu",	0x30ba }, /* ズ */
		{ "kze",	0x30bc }, /* ゼ */
		{ "kzo",	0x30be }, /* ゾ */
		{ "kta",	0x30bf }, /* タ */
		{ "kti",	0x30c1 }, /* チ */
		{ "ktu",	0x30c4 }, /* ツ */
		{ "kte",	0x30c6 }, /* テ */
		{ "kto",	0x30c8 }, /* ト */
		{ "kda",	0x30c0 }, /* ダ */
		{ "kdi",	0x30c2 }, /* ヂ */
		{ "kdu",	0x30c5 }, /* ヅ */
		{ "kde",	0x30c7 }, /* デ */
		{ "kdo",	0x30c9 }, /* ド */
		{ "kna",	0x30ca }, /* ナ */
		{ "kni",	0x30cb }, /* ニ */
		{ "knu",	0x30cc }, /* ヌ */
		{ "kne",	0x30cd }, /* ネ */
		{ "kno",	0x30ce }, /* ノ */
		{ "kha",	0x30cf }, /* ハ */
		{ "khi",	0x30d2 }, /* ヒ */
		{ "khu",	0x30d5 }, /* フ */
		{ "khe",	0x30d8 }, /* ヘ */
		{ "kho",	0x30db }, /* ホ */
		{ "kba",	0x30d0 }, /* バ */
		{ "kbi",	0x30d3 }, /* ビ */
		{ "kbu",	0x30d6 }, /* ブ */
		{ "kbe",	0x30d9 }, /* ベ */
		{ "kbo",	0x30dc }, /* ボ */
		{ "kpa",	0x30d1 }, /* パ */
		{ "kpi",	0x30d4 }, /* ピ */
		{ "kpu",	0x30d7 }, /* プ */
		{ "kpe",	0x30da }, /* ペ */
		{ "kpo",	0x30dd }, /* ポ */
		{ "kma",	0x30de }, /* マ */
		{ "kmi",	0x30df }, /* ミ */
		{ "kmu",	0x30e0 }, /* ム */
		{ "kme",	0x30e1 }, /* メ */
		{ "kmo",	0x30e2 }, /* モ */
		{ "kya",	0x30e4 }, /* ヤ */
		{ "kyu",	0x30e6 }, /* ユ */
		{ "kyo",	0x30e8 }, /* ヨ */
		{ "kra",	0x30e9 }, /* ラ */
		{ "kri",	0x30ea }, /* リ */
		{ "kru",	0x30eb }, /* ル */
		{ "kre",	0x30ec }, /* レ */
		{ "kro",	0x30ed }, /* ロ */
		{ "kwa",	0x30ef }, /* ワ */
		{ "kwi",	0x30f0 }, /* ヰ */
		{ "kwe",	0x30f1 }, /* ヱ */
		{ "kwo",	0x30f2 }, /* ヲ */
		{ "kn",		0x30f3 }, /* ン */
		{ "kvu",	0x30f4 }, /* ヴ */
		{ "kas",	0x30a1 }, /* ァ */
		{ "kis",	0x30a3 }, /* ィ */
		{ "kus",	0x30a5 }, /* ゥ */
		{ "kes",	0x30a7 }, /* ェ */
		{ "kos",	0x30a9 }, /* ォ */
		{ "kkas",	0x30f5 }, /* ヵ */
		{ "kkes",	0x30f6 }, /* ヶ */
		{ "ksa",	0x30b5 }, /* サ */
		{ "ksi",	0x30b7 }, /* シ */
		{ "ksu",	0x30b9 }, /* ス */
		{ "kse",	0x30bb }, /* セ */
		{ "kso",	0x30bd }, /* ソ */
		{ "kyas",	0x30e3 }, /* ャ */
		{ "kyus",	0x30e5 }, /* ュ */
		{ "kyos",	0x30e7 }, /* ョ */
		{ "kwas",	0x30ee }, /* ヮ */
		{ "kq",		0x30c3 }, /* ッ */

		{ "kdot",	0x30FB }, /* ・ */
		{ "kdash",	0x30FC }, /* ー */

		{ "nbsp",	0x00a0 }, /* Non-breaking space */

		/* Symbols: */
		{ "delta",	0x0394 }, /* Δ */
		{ "sigma",	0x03a3 }, /* Σ */
		{ "omega",	0x03a9 }, /* Ω */
		{ "angle",	0x2220 }, /* ∠ */
		{ "whiteheart",	0x2661 }, /* ♡ */
		{ "blackstar",	0x2605 }, /* ★ */
		{ "whitestar",	0x2606 }, /* ☆ */
		{ "flipped-a",	0x2200 }, /* ∀ */
		{ "squared",	0x00b2 }, /* ² */
		{ "cubed",	0x00b3 }, /* ³ */
		{ "oq",		0x201c }, /* “ */
		{ "cq",		0x201d }, /* ” */
		{ "leftarrow",	0x2190 }, /* ← */
		{ "uparrow",	0x2191 }, /* ↑ */
		{ "rightarrow",	0x2192 }, /* → */
		{ "downarrow",	0x2193 }, /* ↓ */
		{ "4thnote",	0x2669 }, /* ♩ */
		{ "8thnote",	0x266A }, /* ♪ */
		{ "b8thnote",	0x266B }, /* ♫ */
		{ "b16thnote",	0x266C }, /* ♬ */
		{ "flat",	0x266D }, /* ♭ */
		{ "natural",	0x266E }, /* ♮ */
		{ "sharp",	0x266F }, /* ♯ */

		/* These are internal-use glyphs; they don't have real Unicode codepoints. */
		{ "up",		INTERNAL },
		{ "down",	INTERNAL },
		{ "left",	INTERNAL },
		{ "right",	INTERNAL },
		{ "downleft",	INTERNAL },
		{ "downright",	INTERNAL },
		{ "upleft",	INTERNAL },
		{ "upright",	INTERNAL },
		{ "center",	INTERNAL },
		{ "menuup",	INTERNAL },
		{ "menudown",	INTERNAL },
		{ "menuleft",	INTERNAL },
		{ "menuright",	INTERNAL },
		{ "start",	INTERNAL },
		{ "doublezeta",	INTERNAL },
		{ "planet",	INTERNAL },
		{ "back",	INTERNAL },
		{ "ok",		INTERNAL },
		{ "nextrow",	INTERNAL },
		{ "select",	INTERNAL },
		/* PlayStation-style controller */
		{ "auxx",	INTERNAL },
		{ "auxtriangle",INTERNAL },
		{ "auxsquare",	INTERNAL },
		{ "auxcircle",	INTERNAL },
		{ "auxl1",	INTERNAL },
		{ "auxl2",	INTERNAL },
		{ "auxl3",	INTERNAL },
		{ "auxr1",	INTERNAL },
		{ "auxr2",	INTERNAL },
		{ "auxr3",	INTERNAL },
		{ "auxselect",	INTERNAL },
		{ "auxstart",	INTERNAL },

		{ NULL, 	0 }
	};

	int iNextInternalUseCodepoint = 0xE000;
	for( unsigned n = 0; aliases[n].str; ++n )
	{
		int iCodepoint = aliases[n].chr;
		if( iCodepoint == INTERNAL )
			iCodepoint = iNextInternalUseCodepoint++;

		CharAliases[aliases[n].str] = iCodepoint;
	}

	for(aliasmap::const_iterator i = CharAliases.begin(); i != CharAliases.end(); ++i)
	{
		RString from = i->first;
		RString to = WcharToUTF8(i->second);
		from.MakeLower();
		CharAliasRepl[from] = to;
	}
}
Ejemplo n.º 16
0
int CheckEnum( lua_State *L, LuaReference &table, int iPos, int iInvalid, const char *szType, bool bAllowInvalid, bool bAllowAnything )
{
	luaL_checkany( L, iPos );

	if( lua_isnil(L, iPos) )
	{
		if( bAllowInvalid )
			return iInvalid;

		LuaHelpers::Push( L, ssprintf("Expected %s; got nil", szType) );
		lua_error( L );
	}

	iPos = LuaHelpers::AbsIndex( L, iPos );

	table.PushSelf( L );
	lua_pushvalue( L, iPos );
	lua_gettable( L, -2 );

	// If not found, check case-insensitively for legacy compatibility
	if( lua_isnil(L, -1) && lua_isstring(L, iPos) ) {
		RString sLower;

		// Get rid of nil value on stack
		lua_pop( L, 1 );

		// Get the string and lowercase it
		lua_pushvalue( L, iPos );
		LuaHelpers::Pop( L, sLower );
		sLower.MakeLower();

		// Try again to read the value
		table.PushSelf( L );
		LuaHelpers::Push( L, sLower );
		lua_gettable( L, -2 );
	}

	// If the result is nil, then a string was passed that is not a member of this enum.  Throw
	// an error.  To specify the invalid value, pass nil.  That way, typos will throw an error,
	// and not silently result in nil, or an out-of-bounds value.
	if( unlikely(lua_isnil(L, -1)) )
	{
		RString sGot;
		if( lua_isstring(L, iPos) )
		{
			/* We were given a string, but it wasn't a valid value for this enum.  Show
			 * the string. */
			lua_pushvalue( L, iPos );
			LuaHelpers::Pop( L, sGot );
			sGot = ssprintf( "\"%s\"", sGot.c_str() );
		}
		else
		{
			/* We didn't get a string.  Show the type. */
			luaL_pushtype( L, iPos );
			LuaHelpers::Pop( L, sGot );
		}
		LuaHelpers::Push( L, ssprintf("Expected %s; got %s", szType, sGot.c_str() ) );
		// There are a couple places where CheckEnum is used outside of a
		// function called from lua.  If we use lua_error from one of them,
		// StepMania crashes out completely.  bAllowAnything allows those places
		// to avoid crashing over theme mistakes.
		if(bAllowAnything)
		{
			RString errmsg;
			LuaHelpers::Pop(L, errmsg);
			LOG->Warn(errmsg.c_str());
			lua_pop(L, 2);
			return iInvalid;
		}
		lua_error( L );
	}
	int iRet = lua_tointeger( L, -1 );
	lua_pop( L, 2 );
	return iRet;
}