2003-02-16 04:01:45 +00:00
|
|
|
#include "global.h"
|
2002-06-23 02:50:33 +00:00
|
|
|
#include "NoteDataWithScoring.h"
|
2005-01-22 18:22:38 +00:00
|
|
|
#include "NoteData.h"
|
2003-12-23 00:26:00 +00:00
|
|
|
#include "StageStats.h"
|
2005-02-16 03:25:45 +00:00
|
|
|
#include "StatsManager.h"
|
2005-04-13 04:08:51 +00:00
|
|
|
#include <float.h>
|
2002-06-23 02:50:33 +00:00
|
|
|
|
2005-01-22 18:22:38 +00:00
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
|
2005-01-22 18:51:51 +00:00
|
|
|
int GetNumTapNotesWithScore( const NoteData &in, TapNoteScore tns, int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW )
|
2002-06-23 02:50:33 +00:00
|
|
|
{
|
2004-05-24 04:26:54 +00:00
|
|
|
int iNumSuccessfulTapNotes = 0;
|
2005-01-22 18:22:38 +00:00
|
|
|
for( int t=0; t<in.GetNumTracks(); t++ )
|
2002-06-23 02:50:33 +00:00
|
|
|
{
|
2005-01-22 18:22:38 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( in, t, r, iStartIndex, iEndIndex )
|
2002-06-23 02:50:33 +00:00
|
|
|
{
|
2005-01-22 18:22:38 +00:00
|
|
|
const TapNote &tn = in.GetTapNote(t, r);
|
2005-01-22 02:10:54 +00:00
|
|
|
if( tn.result.tns >= tns )
|
2004-05-24 04:26:54 +00:00
|
|
|
iNumSuccessfulTapNotes++;
|
2002-06-23 02:50:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-05-24 04:26:54 +00:00
|
|
|
return iNumSuccessfulTapNotes;
|
2002-06-23 02:50:33 +00:00
|
|
|
}
|
|
|
|
|
|
2005-04-13 04:08:51 +00:00
|
|
|
int GetNumNWithScore( const NoteData &in, TapNoteScore tns, int MinTaps, int iStartRow = 0, int iEndRow = MAX_NOTE_ROW )
|
2002-06-23 02:50:33 +00:00
|
|
|
{
|
2003-12-16 02:15:31 +00:00
|
|
|
int iNumSuccessfulDoubles = 0;
|
2005-04-13 04:08:51 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( in, r, iStartRow, iEndRow )
|
2002-06-23 02:50:33 +00:00
|
|
|
{
|
2005-04-13 04:08:51 +00:00
|
|
|
int iNumNotesInRow = in.GetNumTracksWithTapOrHoldHead( r );
|
2005-04-28 00:13:01 +00:00
|
|
|
TapNoteScore tnsRow = NoteDataWithScoring::LastTapNoteResult( in, r ).tns;
|
2005-04-13 04:08:51 +00:00
|
|
|
|
|
|
|
|
if( iNumNotesInRow >= MinTaps && tnsRow >= tns )
|
2002-06-23 02:50:33 +00:00
|
|
|
iNumSuccessfulDoubles++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return iNumSuccessfulDoubles;
|
|
|
|
|
}
|
|
|
|
|
|
2005-04-25 10:01:39 +00:00
|
|
|
int GetNumHoldNotesWithScore( const NoteData &in, TapNote::SubType subType, HoldNoteScore hns )
|
2002-06-23 02:50:33 +00:00
|
|
|
{
|
2005-05-01 05:08:45 +00:00
|
|
|
ASSERT( subType != TapNote::SubType_invalid );
|
|
|
|
|
|
2004-09-29 06:43:57 +00:00
|
|
|
int iNumSuccessfulHolds = 0;
|
2005-01-25 05:02:35 +00:00
|
|
|
for( int t=0; t<in.GetNumTracks(); ++t )
|
2002-06-23 02:50:33 +00:00
|
|
|
{
|
2005-01-25 05:02:35 +00:00
|
|
|
NoteData::TrackMap::const_iterator begin, end;
|
|
|
|
|
in.GetTapNoteRange( t, 0, MAX_NOTE_ROW, begin, end );
|
|
|
|
|
|
|
|
|
|
for( ; begin != end; ++begin )
|
|
|
|
|
{
|
|
|
|
|
const TapNote &tn = begin->second;
|
|
|
|
|
if( tn.type != TapNote::hold_head )
|
|
|
|
|
continue;
|
2005-05-01 05:08:45 +00:00
|
|
|
if( tn.subType != subType )
|
2005-04-25 10:01:39 +00:00
|
|
|
continue;
|
2005-01-25 05:02:35 +00:00
|
|
|
if( tn.HoldResult.hns == hns )
|
|
|
|
|
++iNumSuccessfulHolds;
|
|
|
|
|
}
|
2002-06-23 02:50:33 +00:00
|
|
|
}
|
2005-01-25 05:02:35 +00:00
|
|
|
|
2002-06-23 02:50:33 +00:00
|
|
|
return iNumSuccessfulHolds;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-22 18:51:51 +00:00
|
|
|
int GetSuccessfulMines( const NoteData &in, int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW )
|
2003-12-16 02:15:31 +00:00
|
|
|
{
|
|
|
|
|
int iNumSuccessfulMinesNotes = 0;
|
2005-01-22 18:37:50 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( in, i, iStartIndex, iEndIndex )
|
2003-12-16 02:15:31 +00:00
|
|
|
{
|
2005-01-22 18:22:38 +00:00
|
|
|
for( int t=0; t<in.GetNumTracks(); t++ )
|
2003-12-16 02:15:31 +00:00
|
|
|
{
|
2005-01-22 18:22:38 +00:00
|
|
|
const TapNote &tn = in.GetTapNote(t,i);
|
2005-10-08 00:57:40 +00:00
|
|
|
if( tn.type == TapNote::mine && tn.result.tns == TNS_AvoidMine )
|
2003-12-16 02:15:31 +00:00
|
|
|
iNumSuccessfulMinesNotes++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return iNumSuccessfulMinesNotes;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-16 07:58:37 +00:00
|
|
|
/* See NoteData::GetNumHands(). */
|
2005-01-22 18:51:51 +00:00
|
|
|
int GetSuccessfulHands( const NoteData &in, int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW )
|
2003-12-16 04:00:39 +00:00
|
|
|
{
|
2003-12-16 07:58:37 +00:00
|
|
|
int iNum = 0;
|
2005-01-22 18:37:50 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( in, i, iStartIndex, iEndIndex )
|
2003-12-16 07:58:37 +00:00
|
|
|
{
|
2005-01-22 18:22:38 +00:00
|
|
|
if( !in.RowNeedsHands(i) )
|
2003-12-16 07:58:37 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
bool Missed = false;
|
2005-01-22 18:22:38 +00:00
|
|
|
for( int t=0; t<in.GetNumTracks(); t++ )
|
2003-12-16 07:58:37 +00:00
|
|
|
{
|
2005-01-25 05:02:35 +00:00
|
|
|
const TapNote &tn = in.GetTapNote(t, i);
|
2004-09-12 05:56:24 +00:00
|
|
|
if( tn.type == TapNote::empty )
|
|
|
|
|
continue;
|
|
|
|
|
if( tn.type == TapNote::mine ) // mines don't count
|
2003-12-16 07:58:37 +00:00
|
|
|
continue;
|
2005-10-09 04:30:59 +00:00
|
|
|
if( tn.result.tns <= TNS_W5 )
|
2003-12-16 07:58:37 +00:00
|
|
|
Missed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( Missed )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* Check hold scores. */
|
2005-01-25 05:02:35 +00:00
|
|
|
for( int t=0; t<in.GetNumTracks(); ++t )
|
2003-12-16 07:58:37 +00:00
|
|
|
{
|
2005-01-25 05:02:35 +00:00
|
|
|
int iHeadRow;
|
2005-07-22 00:14:24 +00:00
|
|
|
if( !in.IsHoldNoteAtRow( t, i, &iHeadRow ) )
|
2004-05-07 04:57:29 +00:00
|
|
|
continue;
|
2005-02-08 02:08:04 +00:00
|
|
|
const TapNote &tn = in.GetTapNote( t, iHeadRow );
|
2004-05-07 04:57:29 +00:00
|
|
|
|
|
|
|
|
/* If a hold is released *after* a hands containing it, the hands is
|
|
|
|
|
* still good. So, ignore the judgement and only examine iLastHeldRow
|
|
|
|
|
* to be sure that the hold was still held at the point of this row.
|
|
|
|
|
* (Note that if the hold head tap was missed, then iLastHeldRow == i
|
|
|
|
|
* and this won't fail--but the tap check above will have already failed.) */
|
2005-01-25 05:02:35 +00:00
|
|
|
if( tn.HoldResult.iLastHeldRow < i )
|
2003-12-16 07:58:37 +00:00
|
|
|
Missed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( !Missed )
|
|
|
|
|
iNum++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return iNum;
|
2003-12-16 04:00:39 +00:00
|
|
|
}
|
|
|
|
|
|
2003-03-26 19:34:59 +00:00
|
|
|
/* Return the last tap score of a row: the grade of the tap that completed
|
2003-03-27 18:40:58 +00:00
|
|
|
* the row. If the row has no tap notes, return -1. If any tap notes aren't
|
2005-10-08 00:57:40 +00:00
|
|
|
* graded (any tap is TNS_None) or are missed (TNS_Miss), return it. */
|
2005-01-22 18:22:38 +00:00
|
|
|
int LastTapNoteScoreTrack( const NoteData &in, unsigned iRow )
|
2003-03-26 19:34:59 +00:00
|
|
|
{
|
|
|
|
|
float scoretime = -9999;
|
2003-03-27 00:39:17 +00:00
|
|
|
int best_track = -1;
|
2005-01-22 18:22:38 +00:00
|
|
|
for( int t=0; t<in.GetNumTracks(); t++ )
|
2003-03-26 19:34:59 +00:00
|
|
|
{
|
2003-11-11 07:36:28 +00:00
|
|
|
/* Skip empty tracks and mines */
|
2005-01-22 18:22:38 +00:00
|
|
|
const TapNote &tn = in.GetTapNote( t, iRow );
|
2004-09-12 05:56:24 +00:00
|
|
|
if( tn.type == TapNote::empty || tn.type == TapNote::mine )
|
2003-11-11 07:36:28 +00:00
|
|
|
continue;
|
2003-03-26 19:34:59 +00:00
|
|
|
|
2005-01-22 02:10:54 +00:00
|
|
|
TapNoteScore tns = tn.result.tns;
|
2003-08-10 19:07:54 +00:00
|
|
|
|
2005-10-08 00:57:40 +00:00
|
|
|
if( tns == TNS_Miss || tns == TNS_None )
|
2003-09-27 18:50:38 +00:00
|
|
|
return t;
|
2003-03-26 22:22:44 +00:00
|
|
|
|
2005-01-22 02:10:54 +00:00
|
|
|
float tm = tn.result.fTapNoteOffset;
|
2003-03-26 19:34:59 +00:00
|
|
|
if(tm < scoretime) continue;
|
|
|
|
|
|
|
|
|
|
scoretime = tm;
|
2003-03-27 00:39:17 +00:00
|
|
|
best_track = t;
|
2003-03-26 19:34:59 +00:00
|
|
|
}
|
|
|
|
|
|
2003-03-27 00:39:17 +00:00
|
|
|
return best_track;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-22 18:22:38 +00:00
|
|
|
}
|
2005-04-28 00:13:01 +00:00
|
|
|
|
|
|
|
|
TapNoteResult NoteDataWithScoring::LastTapNoteResult( const NoteData &in, unsigned iRow )
|
2003-03-27 00:39:17 +00:00
|
|
|
{
|
2005-01-22 18:22:38 +00:00
|
|
|
int iTrack = LastTapNoteScoreTrack( in, iRow );
|
|
|
|
|
if( iTrack == -1 )
|
2005-04-28 00:13:01 +00:00
|
|
|
return TapNoteResult();
|
|
|
|
|
return in.GetTapNote(iTrack, iRow).result;
|
2005-01-22 18:22:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Return the minimum tap score of a row. If the row isn't complete (not all
|
2005-10-08 00:57:40 +00:00
|
|
|
* taps have been hit), return TNS_None or TNS_Miss. */
|
2005-01-22 18:22:38 +00:00
|
|
|
TapNoteScore NoteDataWithScoring::MinTapNoteScore( const NoteData &in, unsigned row )
|
|
|
|
|
{
|
2005-10-09 04:30:59 +00:00
|
|
|
TapNoteScore score = TNS_W1;
|
2005-01-22 18:22:38 +00:00
|
|
|
for( int t=0; t<in.GetNumTracks(); t++ )
|
|
|
|
|
{
|
2005-10-08 00:57:40 +00:00
|
|
|
/* Ignore mines, or the score will always be TNS_None. */
|
2005-01-22 18:22:38 +00:00
|
|
|
const TapNote &tn = in.GetTapNote(t, row);
|
2005-01-25 19:12:03 +00:00
|
|
|
if( tn.type == TapNote::empty || tn.type == TapNote::mine )
|
2005-01-22 18:22:38 +00:00
|
|
|
continue;
|
|
|
|
|
score = min( score, tn.result.tns );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return score;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool NoteDataWithScoring::IsRowCompletelyJudged( const NoteData &in, unsigned row )
|
|
|
|
|
{
|
2005-10-08 00:57:40 +00:00
|
|
|
return MinTapNoteScore( in, row ) >= TNS_Miss;
|
2003-03-26 19:34:59 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-22 18:22:38 +00:00
|
|
|
namespace
|
2002-12-17 22:05:17 +00:00
|
|
|
{
|
2005-10-09 04:30:59 +00:00
|
|
|
/* Return the ratio of actual to possible Bs. */
|
2005-01-22 18:22:38 +00:00
|
|
|
float GetActualStreamRadarValue( const NoteData &in, float fSongSeconds, PlayerNumber pn )
|
2002-06-23 02:50:33 +00:00
|
|
|
{
|
2005-01-22 18:22:38 +00:00
|
|
|
int iTotalSteps = in.GetNumTapNotes();
|
2005-01-22 16:12:31 +00:00
|
|
|
if( iTotalSteps == 0 )
|
|
|
|
|
return 1.0f;
|
2003-07-24 13:35:20 +00:00
|
|
|
|
2005-10-09 04:30:59 +00:00
|
|
|
const int iW2s = GetNumTapNotesWithScore( in, TNS_W2 );
|
|
|
|
|
return clamp( float(iW2s)/iTotalSteps, 0.0f, 1.0f );
|
2002-06-23 02:50:33 +00:00
|
|
|
}
|
|
|
|
|
|
2005-04-25 11:33:17 +00:00
|
|
|
/* Return the ratio of actual combo to max combo. */
|
2005-01-22 18:22:38 +00:00
|
|
|
float GetActualVoltageRadarValue( const NoteData &in, float fSongSeconds, PlayerNumber pn )
|
2002-06-23 02:50:33 +00:00
|
|
|
{
|
2005-02-16 03:25:45 +00:00
|
|
|
/* STATSMAN->m_CurStageStats.iMaxCombo is unrelated to GetNumTapNotes: m_bComboContinuesBetweenSongs
|
2003-11-02 17:06:25 +00:00
|
|
|
* might be on, and the way combo is counted varies depending on the mode and score
|
|
|
|
|
* keeper. Instead, let's use the length of the longest recorded combo. This is
|
|
|
|
|
* only subtly different: it's the percent of the song the longest combo took to get. */
|
2005-02-16 03:25:45 +00:00
|
|
|
const PlayerStageStats::Combo_t MaxCombo = STATSMAN->m_CurStageStats.m_player[pn].GetMaxCombo();
|
|
|
|
|
float fComboPercent = SCALE( MaxCombo.fSizeSeconds, 0, STATSMAN->m_CurStageStats.m_player[pn].fLastSecond-STATSMAN->m_CurStageStats.m_player[pn].fFirstSecond, 0.0f, 1.0f );
|
2004-02-23 23:21:14 +00:00
|
|
|
return clamp( fComboPercent, 0.0f, 1.0f );
|
2002-06-23 02:50:33 +00:00
|
|
|
}
|
|
|
|
|
|
2005-10-09 04:30:59 +00:00
|
|
|
/* Return the ratio of actual to possible W2s on jumps. */
|
2005-01-22 18:22:38 +00:00
|
|
|
float GetActualAirRadarValue( const NoteData &in, float fSongSeconds, PlayerNumber pn )
|
2002-06-23 02:50:33 +00:00
|
|
|
{
|
2005-03-20 20:15:41 +00:00
|
|
|
const int iTotalDoubles = in.GetNumJumps();
|
2005-01-22 16:12:31 +00:00
|
|
|
if( iTotalDoubles == 0 )
|
|
|
|
|
return 1.0f; // no jumps in song
|
2003-07-24 13:35:20 +00:00
|
|
|
|
2003-09-25 01:21:01 +00:00
|
|
|
// number of doubles
|
2005-10-09 04:30:59 +00:00
|
|
|
const int iNumDoubles = GetNumNWithScore( in, TNS_W2, 2 );
|
2003-09-25 01:21:01 +00:00
|
|
|
return clamp( (float)iNumDoubles / iTotalDoubles, 0.0f, 1.0f );
|
2002-06-23 02:50:33 +00:00
|
|
|
}
|
|
|
|
|
|
2005-04-25 11:33:17 +00:00
|
|
|
/* Return the ratio of actual to possible dance points. */
|
2005-01-22 18:22:38 +00:00
|
|
|
float GetActualChaosRadarValue( const NoteData &in, float fSongSeconds, PlayerNumber pn )
|
2002-06-23 02:50:33 +00:00
|
|
|
{
|
2005-02-16 03:25:45 +00:00
|
|
|
const int iPossibleDP = STATSMAN->m_CurStageStats.m_player[pn].iPossibleDancePoints;
|
2005-01-22 16:12:31 +00:00
|
|
|
if ( iPossibleDP == 0 )
|
2003-11-02 17:06:25 +00:00
|
|
|
return 1;
|
2003-07-24 13:35:20 +00:00
|
|
|
|
2005-02-16 03:25:45 +00:00
|
|
|
const int ActualDP = STATSMAN->m_CurStageStats.m_player[pn].iActualDancePoints;
|
2005-01-22 16:12:31 +00:00
|
|
|
return clamp( float(ActualDP)/iPossibleDP, 0.0f, 1.0f );
|
2002-06-23 02:50:33 +00:00
|
|
|
}
|
|
|
|
|
|
2005-04-25 11:33:17 +00:00
|
|
|
/* Return the ratio of actual to possible successful holds. */
|
2005-01-22 18:22:38 +00:00
|
|
|
float GetActualFreezeRadarValue( const NoteData &in, float fSongSeconds, PlayerNumber pn )
|
2002-06-23 02:50:33 +00:00
|
|
|
{
|
|
|
|
|
// number of hold steps
|
2005-01-22 18:22:38 +00:00
|
|
|
const int iTotalHolds = in.GetNumHoldNotes();
|
2005-01-22 16:12:31 +00:00
|
|
|
if( iTotalHolds == 0 )
|
2003-09-25 01:21:01 +00:00
|
|
|
return 1.0f;
|
|
|
|
|
|
2005-05-01 05:08:45 +00:00
|
|
|
const int ActualHolds =
|
2005-10-08 00:57:40 +00:00
|
|
|
GetNumHoldNotesWithScore( in, TapNote::hold_head_hold, HNS_Held ) +
|
|
|
|
|
GetNumHoldNotesWithScore( in, TapNote::hold_head_roll, HNS_Held );
|
2005-01-22 16:12:31 +00:00
|
|
|
return clamp( float(ActualHolds) / iTotalHolds, 0.0f, 1.0f );
|
2002-12-18 22:24:34 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-22 18:22:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void NoteDataWithScoring::GetActualRadarValues( const NoteData &in, PlayerNumber pn, float fSongSeconds, RadarValues& out )
|
|
|
|
|
{
|
|
|
|
|
// The for loop and the assert are used to ensure that all fields of
|
|
|
|
|
// RadarValue get set in here.
|
|
|
|
|
FOREACH_RadarCategory( rc )
|
|
|
|
|
{
|
|
|
|
|
switch( rc )
|
|
|
|
|
{
|
|
|
|
|
case RADAR_STREAM: out[rc] = GetActualStreamRadarValue( in, fSongSeconds, pn ); break;
|
|
|
|
|
case RADAR_VOLTAGE: out[rc] = GetActualVoltageRadarValue( in, fSongSeconds, pn ); break;
|
|
|
|
|
case RADAR_AIR: out[rc] = GetActualAirRadarValue( in, fSongSeconds, pn ); break;
|
|
|
|
|
case RADAR_FREEZE: out[rc] = GetActualFreezeRadarValue( in, fSongSeconds, pn ); break;
|
|
|
|
|
case RADAR_CHAOS: out[rc] = GetActualChaosRadarValue( in, fSongSeconds, pn ); break;
|
2005-10-09 04:30:59 +00:00
|
|
|
case RADAR_NUM_TAPS_AND_HOLDS: out[rc] = (float) GetNumNWithScore( in, TNS_W4, 1 ); break;
|
|
|
|
|
case RADAR_NUM_JUMPS: out[rc] = (float) GetNumNWithScore( in, TNS_W4, 2 ); break;
|
2005-10-08 00:57:40 +00:00
|
|
|
case RADAR_NUM_HOLDS: out[rc] = (float) GetNumHoldNotesWithScore( in, TapNote::hold_head_hold, HNS_Held ); break;
|
2005-01-22 18:22:38 +00:00
|
|
|
case RADAR_NUM_MINES: out[rc] = (float) GetSuccessfulMines( in ); break;
|
|
|
|
|
case RADAR_NUM_HANDS: out[rc] = (float) GetSuccessfulHands( in ); break;
|
2005-10-08 00:57:40 +00:00
|
|
|
case RADAR_NUM_ROLLS: out[rc] = (float) GetNumHoldNotesWithScore( in, TapNote::hold_head_roll, HNS_Held ); break;
|
2005-01-22 18:22:38 +00:00
|
|
|
default: ASSERT(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-05-31 22:42:12 +00:00
|
|
|
/*
|
|
|
|
|
* (c) 2001-2004 Chris Danford, Glenn Maynard
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the
|
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
|
* distribute, and/or sell copies of the Software, and to permit persons to
|
|
|
|
|
* whom the Software is furnished to do so, provided that the above
|
|
|
|
|
* copyright notice(s) and this permission notice appear in all copies of
|
|
|
|
|
* the Software and that both the above copyright notice(s) and this
|
|
|
|
|
* permission notice appear in supporting documentation.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
|
|
|
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
|
|
|
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
|
|
|
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
|
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
*/
|