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; }