Пример #1
0
static sboolean Music_ParseLeveldata(const char *psLevelName)
{
	sboolean bReturn = qfalse;

	if (MusicData == NULL)
	{
		MusicData = new MusicData_t;
	}
	
		// already got this data?
	//
	if (MusicData->size() && !Q_stricmp(psLevelName,gsLevelNameForCompare.c_str()))
	{
		return qtrue;
	}
	
	MusicData->clear();	

	char sLevelName[MAX_QPATH];
	Q_strncpyz(sLevelName,psLevelName,sizeof(sLevelName));
	
	gsLevelNameForLoad		= sLevelName;	// harmless to init here even if we fail to parse dms.dat file	
	gsLevelNameForCompare	= sLevelName;	// harmless to init here even if we fail to parse dms.dat file	
	gsLevelNameForBossLoad	= sLevelName;	// harmless to init here even if we fail to parse dms.dat file	

	char *pText = NULL;
	/*int iTotalBytesLoaded = */FS_ReadFile(sFILENAME_DMS, (void **)&pText );			
	if (pText) 
	{
		char *psStrippedText = StripTrailingWhiteSpaceOnEveryLine(pText);		
		CGenericParser2 Parser;
		char *psDataPtr = psStrippedText;	// because ptr gets advanced, so we supply a clone that GP can alter
		if (Parser.Parse(&psDataPtr, true))
		{
			CGPGroup *pFileGroup = Parser.GetBaseParseGroup();
			if (pFileGroup)
			{
				CGPGroup *pgMusicFiles = pFileGroup->FindSubGroup(sKEY_MUSICFILES);
				if (pgMusicFiles)
				{
					CGPGroup *pgLevelMusic = pFileGroup->FindSubGroup(sKEY_LEVELMUSIC);

					if (pgLevelMusic)
					{
						CGPGroup *pgThisLevelMusic = NULL;
						//
						// check for new USE keyword...
						//
						int iSanityLimit = 0;
						sstring_t sSearchName(sLevelName);

						while (sSearchName.c_str()[0] && iSanityLimit < 10)
						{
							gsLevelNameForLoad		= sSearchName;
							gsLevelNameForBossLoad	= sSearchName;
							pgThisLevelMusic = pgLevelMusic->FindSubGroup(sSearchName.c_str());

							if (pgThisLevelMusic)
							{
								CGPValue *pValue = pgThisLevelMusic->FindPair(sKEY_USES);
								if (pValue)
								{
									// re-search using the USE param...
									//									
									sSearchName = pValue->GetTopValue();
									iSanityLimit++;
//									Com_DPrintf("Using \"%s\"\n",sSearchName.c_str());
								}
								else
								{
									// no new USE keyword found...
									//
									sSearchName = "";
								}
							}
							else
							{
								// level entry not found...
								//
								break;
							}
						}

						// now go ahead and use the final music set we've decided on...
						//
						if (pgThisLevelMusic && iSanityLimit < 10)
						{
							// these are optional fields, so see which ones we find...
							//
							LPCSTR psName_Explore = NULL;
							LPCSTR psName_Action  = NULL;
							LPCSTR psName_Boss	  = NULL;
							LPCSTR psName_Death	  = NULL;
							//
							LPCSTR psName_UseBoss = NULL;

							for (CGPValue *pValue = pgThisLevelMusic->GetPairs(); pValue; pValue = pValue->GetNext())
							{
								LPCSTR psKey	= pValue->GetName();
								LPCSTR psValue	= pValue->GetTopValue();								

								if (Q_stricmp(psValue,sKEY_PLACEHOLDER))	// ignore "placeholder" items
								{
									if (!Q_stricmp(psKey,sKEY_EXPLORE))
									{
										psName_Explore = psValue;
									}
									else
									if (!Q_stricmp(psKey,sKEY_ACTION))
									{
										psName_Action  = psValue;
									}
									else
									if (!Q_stricmp(psKey,sKEY_USEBOSS))
									{
										psName_UseBoss = psValue;
									}
									else
									if (!Q_stricmp(psKey,sKEY_BOSS))
									{
										psName_Boss = psValue;
									}
									else
									if (!Q_stricmp(psKey,sKEY_DEATH))
									{
										psName_Death = psValue;
									}
								}
							}
									
							bReturn = qtrue;	// defualt to ON now, so I can turn it off if "useboss" fails

							if (psName_UseBoss)
							{
								CGPGroup *pgLevelMusicOfBoss = pgLevelMusic->FindSubGroup(psName_UseBoss);
								if (pgLevelMusicOfBoss)
								{
									CGPValue *pValueBoss = pgLevelMusicOfBoss->FindPair(sKEY_BOSS);
									if (pValueBoss)
									{	
										psName_Boss = pValueBoss->GetTopValue();
										gsLevelNameForBossLoad = psName_UseBoss;
									}
									else
									{
										MUSIC_PARSE_ERROR(va("'useboss' \"%s\" has no \"boss\" entry!\n",psName_UseBoss));
										bReturn = qfalse;
									}
								}
								else
								{
									MUSIC_PARSE_ERROR(va("Unable to find 'useboss' entry \"%s\"\n",psName_UseBoss));									
									bReturn = qfalse;
								}
							}


							// done this way in case I want to conditionally pass any bools depending on music type...
							//
							if (bReturn && psName_Explore)
							{
								bReturn = Music_ParseMusic(Parser, MusicData, pgMusicFiles, psName_Explore,	sKEY_EXPLORE, eBGRNDTRACK_EXPLORE);
							}
							if (bReturn && psName_Action)
							{
								bReturn = Music_ParseMusic(Parser, MusicData, pgMusicFiles, psName_Action,	sKEY_ACTION,  eBGRNDTRACK_ACTION);
							}
							if (bReturn && psName_Boss)
							{
								bReturn = Music_ParseMusic(Parser, MusicData, pgMusicFiles, psName_Boss,	sKEY_BOSS,    eBGRNDTRACK_BOSS);
							}
							if (bReturn /*&& psName_Death*/)	// LAST MINUTE HACK!!, always force in some death music!!!!
							{
								//bReturn = Music_ParseMusic(Parser, MusicData, pgMusicFiles, psName_Death,	sKEY_DEATH,   eBGRNDTRACK_DEATH);

								MusicFile_t m;
											m.sFileNameBase = "death_music";
								(*MusicData)[ sKEY_DEATH ] = m;
							}
						}
						else
						{
							MUSIC_PARSE_WARNING(va("Unable to find entry for \"%s\" in \"%s\"\n",sLevelName,sFILENAME_DMS));
						}
					}
					else
					{
						MUSIC_PARSE_ERROR(va("Unable to find subgroup \"%s\"\n",sKEY_LEVELMUSIC));
					}
				}
				else
				{
					MUSIC_PARSE_ERROR(va("Unable to find subgroup \"%s\"\n",sKEY_MUSICFILES));
				}
			}
			else
			{
				MUSIC_PARSE_ERROR( "Error calling GP2.GetBaseParseGroup()\n" );
			}
		}
		else
		{
			MUSIC_PARSE_ERROR( "Error using GP to parse file\n" );
		}

		Z_Free(psStrippedText);
		FS_FreeFile( pText );
	}
	else
	{
		MUSIC_PARSE_ERROR( "Unable to even read main file\n" );	// file name specified in error message
	}

	if (bReturn)
	{
		// sort exit points, and do some error checking...
		//
		for (MusicData_t::iterator itMusicData = MusicData->begin(); itMusicData != MusicData->end(); ++itMusicData)
		{
			LPCSTR psMusicStateType	= (*itMusicData).first.c_str();
			MusicFile_t &MusicFile	= (*itMusicData).second;

			// kludge up an enum, only interested in boss or not at the moment, so...
			//
			MusicState_e eMusicState = !stricmp(psMusicStateType,"boss") ? eBGRNDTRACK_BOSS : !stricmp(psMusicStateType,"death") ? eBGRNDTRACK_DEATH : eBGRNDTRACK_EXPLORE;

			if (!MusicFile.MusicExitTimes.empty())
			{
				sort(MusicFile.MusicExitTimes.begin(),MusicFile.MusicExitTimes.end());
			}

			// check music exists...
			//
			LPCSTR psMusicFileName = Music_BuildFileName( MusicFile.sFileNameBase.c_str(), eMusicState );
			if (!S_FileExists( psMusicFileName ))
			{
				MUSIC_PARSE_ERROR(va("Music file \"%s\" not found!\n",psMusicFileName));
				return qfalse;		// have to return, because music data destroyed now
			}

			// check all transition music pieces exist, and that entry points into new pieces after transitions also exist...
			//
			for (int iExitPoint=0; iExitPoint < MusicFile.MusicExitPoints.size(); iExitPoint++)
			{
				MusicExitPoint_t &MusicExitPoint = MusicFile.MusicExitPoints[ iExitPoint ];

				LPCSTR psTransitionFileName = Music_BuildFileName( MusicExitPoint.sNextFile.c_str(), eMusicState );
				if (!S_FileExists( psTransitionFileName ))
				{
					MUSIC_PARSE_ERROR(va("Transition file \"%s\" (entry \"%s\" ) not found!\n",psTransitionFileName, MusicExitPoint.sNextFile.c_str()));
					return qfalse;		// have to return, because music data destroyed now
				}

				LPCSTR psNextMark = MusicExitPoint.sNextMark.c_str();
				if (strlen(psNextMark))	// always NZ ptr
				{
					// then this must be "action" music under current rules...
					//					
					assert( !strcmp(psMusicStateType, Music_BaseStateToString(eBGRNDTRACK_ACTION) ? Music_BaseStateToString(eBGRNDTRACK_ACTION):"") );
					//
					// does this marker exist in the explore piece?
					//					
					MusicData_t::iterator itExploreMusicData = MusicData->find( Music_BaseStateToString(eBGRNDTRACK_EXPLORE) );
					if (itExploreMusicData != MusicData->end())
					{
						MusicFile_t &MusicFile_Explore = (*itExploreMusicData).second;

						if (!MusicFile_Explore.MusicEntryTimes.count(psNextMark))
						{
							MUSIC_PARSE_ERROR( va("Unable to find entry point \"%s\" in description for \"%s\"\n",psNextMark,MusicFile_Explore.sFileNameBase.c_str()) );
							return qfalse;		// have to return, because music data destroyed now
						}
					}
					else
					{
						MUSIC_PARSE_ERROR( va("Unable to find %s piece to match \"%s\"\n", Music_BaseStateToString(eBGRNDTRACK_EXPLORE), MusicFile.sFileNameBase.c_str() ) );
						return qfalse;		// have to return, because music data destroyed now
					}
				}
			}
		}
	}
	
#ifdef _DEBUG
/*
	// dump the whole thing out to prove it was read in ok...
	//
	if (bReturn)
	{
		for (MusicData_t::iterator itMusicData = MusicData->begin(); itMusicData != MusicData->end(); ++itMusicData)
		{
			LPCSTR psMusicState		= (*itMusicData).first.c_str();
			MusicFile_t &MusicFile	= (*itMusicData).second;

			OutputDebugString(va("Music State:  \"%s\",  File: \"%s\"\n",psMusicState, MusicFile.sFileNameBase.c_str()));

			// entry times...
			//
			for (MusicEntryTimes_t::iterator itEntryTimes = MusicFile.MusicEntryTimes.begin(); itEntryTimes != MusicFile.MusicEntryTimes.end(); ++itEntryTimes)
			{
				LPCSTR	psMarkerName	= (*itEntryTimes).first.c_str();
				float	fEntryTime		= (*itEntryTimes).second;

				OutputDebugString(va("Entry time for \"%s\": %f\n", psMarkerName, fEntryTime));
			}

			// exit points...
			//
			for (int i=0; i<MusicFile.MusicExitPoints.size(); i++)
			{
				MusicExitPoint_t &MusicExitPoint = MusicFile.MusicExitPoints[i];

				OutputDebugString(va("Exit point %d:	sNextFile: \"%s\", sNextMark: \"%s\"\n",i,MusicExitPoint.sNextFile.c_str(),MusicExitPoint.sNextMark.c_str()));
			}

			// exit times...
			//
			for (i=0; i<MusicFile.MusicExitTimes.size(); i++)
			{
				MusicExitTime_t &MusicExitTime = MusicFile.MusicExitTimes[i];
				
				OutputDebugString(va("Exit time %d:		fTime: %f, iExitPoint: %d\n",i,MusicExitTime.fTime,MusicExitTime.iExitPoint));
			}
		}
	}
*/
#endif

	return bReturn;
}
Пример #2
0
static qboolean Music_ParseLeveldata( gsl::czstring psLevelName )
{
	qboolean bReturn = qfalse;

	if (MusicData == NULL)
	{
		// sorry vv, false leaks make it hard to find true leaks
		static MusicData_t singleton;
		MusicData = &singleton;
	}

	// already got this data?
	//
	if (MusicData->size() && !Q_stricmp(psLevelName,gsLevelNameForCompare.c_str()))
	{
		return qtrue;
	}

	MusicData->clear();

	char sLevelName[MAX_QPATH];
	Q_strncpyz(sLevelName,psLevelName,sizeof(sLevelName));

	gsLevelNameForLoad		= sLevelName;	// harmless to init here even if we fail to parse dms.dat file
	gsLevelNameForCompare	= sLevelName;	// harmless to init here even if we fail to parse dms.dat file
	gsLevelNameForBossLoad	= sLevelName;	// harmless to init here even if we fail to parse dms.dat file

	gsl::czstring filename = sFILENAME_DMS;
	CGenericParser2 Parser;
	if( !Parser.Parse( filename ) )
	{
		Music_Parse_Error( filename, "Error using GP to parse file\n" );
	}
	else
	{
		const CGPGroup& pFileGroup = Parser.GetBaseParseGroup();
		const CGPGroup* pgMusicFiles = pFileGroup.FindSubGroup( sKEY_MUSICFILES );
		if( !pgMusicFiles )
		{
			Music_Parse_Error(filename, build_string( "Unable to find subgroup \"", sKEY_MUSICFILES ,"\"\n" ) );
		}
		else
		{
			const CGPGroup* pgLevelMusic = pFileGroup.FindSubGroup( sKEY_LEVELMUSIC );

			if( !pgLevelMusic )
			{
				Music_Parse_Error( filename, build_string( "Unable to find subgroup \"", sKEY_MUSICFILES, "\"\n" ) );
			}
			else
			{
				const CGPGroup *pgThisLevelMusic = nullptr;
				//
				// check for new USE keyword...
				//
				int steps = 0;
				gsl::cstring_view searchName{ &sLevelName[ 0 ], &sLevelName[ strlen( &sLevelName[ 0 ] ) ] };

				const int sanityLimit = 10;
				while( !searchName.empty() && steps < sanityLimit )
				{
					gsLevelNameForLoad = StringViewToSString( searchName );
					gsLevelNameForBossLoad = gsLevelNameForLoad;
					pgThisLevelMusic = pgLevelMusic->FindSubGroup( searchName );

					if( pgThisLevelMusic )
					{
						const CGPProperty* pValue = pgThisLevelMusic->FindProperty( sKEY_USES );
						if( pValue )
						{
							// re-search using the USE param...
							//
							searchName = pValue->GetTopValue();
							steps++;
							//									Com_DPrintf("Using \"%s\"\n",sSearchName.c_str());
						}
						else
						{
							// no new USE keyword found...
							//
							searchName = {};
						}
					}
					else
					{
						// level entry not found...
						//
						break;
					}
				}

				// now go ahead and use the final music set we've decided on...
				//
				if( !pgThisLevelMusic || steps >= sanityLimit )
				{
					Music_Parse_Warning( build_string( "Unable to find entry for \"", sLevelName, "\" in \"", filename, "\"\n" ) );
				}
				else
				{
					// these are optional fields, so see which ones we find...
					//
					gsl::cstring_view psName_Explore;
					gsl::cstring_view psName_Action;
					gsl::cstring_view psName_Boss;
					gsl::cstring_view psName_UseBoss;

					for( auto& prop : pgThisLevelMusic->GetProperties() )
					{
						auto& key = prop.GetName();
						auto& value = prop.GetTopValue();

						if( Q::stricmp( value, sKEY_PLACEHOLDER ) == Q::Ordering::EQ )
						{
							// ignore "placeholder" items
							continue;
						}

						if( Q::stricmp( key, sKEY_EXPLORE ) == Q::Ordering::EQ )
						{
							psName_Explore = value;
						}
						else if( Q::stricmp( key, sKEY_ACTION ) == Q::Ordering::EQ )
						{
							psName_Action = value;
						}
						else if( Q::stricmp( key, sKEY_USEBOSS ) == Q::Ordering::EQ )
						{
							psName_UseBoss = value;
						}
						else if( Q::stricmp( key, sKEY_BOSS ) == Q::Ordering::EQ )
						{
							psName_Boss = value;
						}
					}

					bReturn = qtrue;	// defualt to ON now, so I can turn it off if "useboss" fails

					if( !psName_UseBoss.empty() )
					{
						const CGPGroup *pgLevelMusicOfBoss = pgLevelMusic->FindSubGroup( psName_UseBoss );
						if( !pgLevelMusicOfBoss )
						{
							Music_Parse_Error( filename, build_string( "Unable to find 'useboss' entry \"", psName_UseBoss, "\"\n", psName_UseBoss ) );
							bReturn = qfalse;
						}
						else
						{
							const CGPProperty *pValueBoss = pgLevelMusicOfBoss->FindProperty( sKEY_BOSS );
							if( pValueBoss )
							{
								Music_Parse_Error( filename, build_string( "'useboss' \"", psName_UseBoss, "\" has no \"boss\" entry!\n" ) );
								bReturn = qfalse;
							}
							else
							{
								psName_Boss = pValueBoss->GetTopValue();
								gsLevelNameForBossLoad = StringViewToSString( psName_UseBoss );
							}
						}
					}


					// done this way in case I want to conditionally pass any bools depending on music type...
					//
					if( bReturn && psName_Explore )
					{
						bReturn = Music_ParseMusic( filename, Parser, MusicData, *pgMusicFiles, psName_Explore, sKEY_EXPLORE, eBGRNDTRACK_EXPLORE );
					}
					if( bReturn && psName_Action )
					{
						bReturn = Music_ParseMusic( filename, Parser, MusicData, *pgMusicFiles, psName_Action, sKEY_ACTION, eBGRNDTRACK_ACTION );
					}
					if( bReturn && psName_Boss )
					{
						bReturn = Music_ParseMusic( filename, Parser, MusicData, *pgMusicFiles, psName_Boss, sKEY_BOSS, eBGRNDTRACK_BOSS );
					}
					if( bReturn /*&& psName_Death*/ )	// LAST MINUTE HACK!!, always force in some death music!!!!
					{
						//bReturn = Music_ParseMusic(Parser, MusicData, pgMusicFiles, psName_Death,	sKEY_DEATH,   eBGRNDTRACK_DEATH);

						MusicFile_t m;
						m.sFileNameBase = "death_music";
						( *MusicData )[ "death" ] = m;
					}
				}
			}
		}
	}

	if (bReturn)
	{
		// sort exit points, and do some error checking...
		//
		for (MusicData_t::iterator itMusicData = MusicData->begin(); itMusicData != MusicData->end(); ++itMusicData)
		{
			const char *psMusicStateType	= (*itMusicData).first.c_str();
			MusicFile_t &MusicFile	= (*itMusicData).second;

			// kludge up an enum, only interested in boss or not at the moment, so...
			//
			MusicState_e eMusicState = !Q_stricmp(psMusicStateType,"boss") ? eBGRNDTRACK_BOSS : !Q_stricmp(psMusicStateType,"death") ? eBGRNDTRACK_DEATH : eBGRNDTRACK_EXPLORE;

			if (!MusicFile.MusicExitTimes.empty())
			{
				sort(MusicFile.MusicExitTimes.begin(),MusicFile.MusicExitTimes.end());
			}

			// check music exists...
			//
			const char *psMusicFileName = Music_BuildFileName( MusicFile.sFileNameBase.c_str(), eMusicState );
			if (!S_FileExists( psMusicFileName ))
			{
				Music_Parse_Error( filename, build_string( "Music file \"", psMusicFileName, "\" not found!\n" ) );
				return qfalse;		// have to return, because music data destroyed now
			}

			// check all transition music pieces exist, and that entry points into new pieces after transitions also exist...
			//
			for (size_t iExitPoint=0; iExitPoint < MusicFile.MusicExitPoints.size(); iExitPoint++)
			{
				MusicExitPoint_t &MusicExitPoint = MusicFile.MusicExitPoints[ iExitPoint ];

				const char *psTransitionFileName = Music_BuildFileName( MusicExitPoint.sNextFile.c_str(), eMusicState );
				if (!S_FileExists( psTransitionFileName ))
				{
					Music_Parse_Error( filename, build_string( "Transition file \"", psTransitionFileName, "\" (entry \"", MusicExitPoint.sNextFile.c_str(), "\" ) not found!\n" ) );
					return qfalse;		// have to return, because music data destroyed now
				}

				const char *psNextMark = MusicExitPoint.sNextMark.c_str();
				if (strlen(psNextMark))	// always NZ ptr
				{
					// then this must be "action" music under current rules...
					//
					assert( !strcmp(psMusicStateType, Music_BaseStateToString(eBGRNDTRACK_ACTION) ? Music_BaseStateToString(eBGRNDTRACK_ACTION):"") );
					//
					// does this marker exist in the explore piece?
					//
					MusicData_t::iterator itExploreMusicData = MusicData->find( Music_BaseStateToString(eBGRNDTRACK_EXPLORE) );
					if (itExploreMusicData != MusicData->end())
					{
						MusicFile_t &MusicFile_Explore = (*itExploreMusicData).second;

						if (!MusicFile_Explore.MusicEntryTimes.count(psNextMark))
						{
							Music_Parse_Error( filename, build_string( "Unable to find entry point \"", psNextMark, "\" in description for \"", MusicFile_Explore.sFileNameBase.c_str(), "\"\n" ) );
							return qfalse;		// have to return, because music data destroyed now
						}
					}
					else
					{
						Music_Parse_Error( filename, build_string( "Unable to find ", Music_BaseStateToString( eBGRNDTRACK_EXPLORE ), " piece to match \"", MusicFile.sFileNameBase.c_str(), "\"\n" ) );
						return qfalse;		// have to return, because music data destroyed now
					}
				}
			}
		}
	}

	return bReturn;
}