+// ServerList interface
+serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
+serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
+
+serverlist_infofield_t serverlist_sortbyfield;
+qboolean serverlist_sortdescending;
+
+int serverlist_viewcount = 0;
+serverlist_entry_t *serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
+
+int serverlist_cachecount;
+serverlist_entry_t serverlist_cache[SERVERLIST_TOTALSIZE];
+
+qboolean serverlist_consoleoutput;
+
+// helper function to insert a value into the viewset
+// spare entries will be removed
+static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
+{
+ int i;
+ if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
+ i = serverlist_viewcount++;
+ } else {
+ i = SERVERLIST_VIEWLISTSIZE - 1;
+ }
+
+ for( ; i > index ; i-- )
+ serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
+
+ serverlist_viewlist[index] = entry;
+}
+
+// we suppose serverlist_viewcount to be valid, ie > 0
+static void _ServerList_ViewList_Helper_Remove( int index )
+{
+ serverlist_viewcount--;
+ for( ; index < serverlist_viewcount ; index++ )
+ serverlist_viewlist[index] = serverlist_viewlist[index + 1];
+}
+
+// returns true if A should be inserted before B
+static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
+{
+ int result = 0; // > 0 if for numbers A > B and for text if A < B
+
+ switch( serverlist_sortbyfield ) {
+ case SLIF_PING:
+ result = A->info.ping - B->info.ping;
+ break;
+ case SLIF_MAXPLAYERS:
+ result = A->info.maxplayers - B->info.maxplayers;
+ break;
+ case SLIF_NUMPLAYERS:
+ result = A->info.numplayers - B->info.numplayers;
+ break;
+ case SLIF_PROTOCOL:
+ result = A->info.protocol - B->info.protocol;
+ break;
+ case SLIF_CNAME:
+ result = strcmp( B->info.cname, A->info.cname );
+ break;
+ case SLIF_GAME:
+ result = strcmp( B->info.game, A->info.game );
+ break;
+ case SLIF_MAP:
+ result = strcmp( B->info.map, A->info.map );
+ break;
+ case SLIF_MOD:
+ result = strcmp( B->info.mod, A->info.mod );
+ break;
+ case SLIF_NAME:
+ result = strcmp( B->info.name, A->info.name );
+ break;
+ default:
+ Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
+ break;
+ }
+
+ if( serverlist_sortdescending )
+ return result > 0;
+ return result < 0;
+}
+
+static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
+{
+ // This should actually be done with some intermediate and end-of-function return
+ switch( op ) {
+ case SLMO_LESS:
+ return A < B;
+ case SLMO_LESSEQUAL:
+ return A <= B;
+ case SLMO_EQUAL:
+ return A == B;
+ case SLMO_GREATER:
+ return A > B;
+ case SLMO_NOTEQUAL:
+ return A != B;
+ case SLMO_GREATEREQUAL:
+ case SLMO_CONTAINS:
+ case SLMO_NOTCONTAIN:
+ return A >= B;
+ default:
+ Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
+ return false;
+ }
+}
+
+static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
+{
+ int i;
+ char bufferA[ 256 ], bufferB[ 256 ]; // should be more than enough
+ for (i = 0;i < sizeof(bufferA)-1 && A[i];i++)
+ bufferA[i] = (A[i] >= 'A' && A[i] <= 'Z') ? (A[i] + 'a' - 'A') : A[i];
+ bufferA[i] = 0;
+ for (i = 0;i < sizeof(bufferB)-1 && B[i];i++)
+ bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
+ bufferB[i] = 0;
+
+ // Same here, also using an intermediate & final return would be more appropriate
+ // A info B mask
+ switch( op ) {
+ case SLMO_CONTAINS:
+ return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
+ case SLMO_NOTCONTAIN:
+ return !*bufferB || !strstr( bufferA, bufferB );
+ case SLMO_LESS:
+ return strcmp( bufferA, bufferB ) < 0;
+ case SLMO_LESSEQUAL:
+ return strcmp( bufferA, bufferB ) <= 0;
+ case SLMO_EQUAL:
+ return strcmp( bufferA, bufferB ) == 0;
+ case SLMO_GREATER:
+ return strcmp( bufferA, bufferB ) > 0;
+ case SLMO_NOTEQUAL:
+ return strcmp( bufferA, bufferB ) != 0;
+ case SLMO_GREATEREQUAL:
+ return strcmp( bufferA, bufferB ) >= 0;
+ default:
+ Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
+ return false;
+ }
+}
+
+static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
+{
+ if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
+ return false;
+ if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
+ return false;
+ if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
+ return false;
+ if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
+ return false;
+ if( *mask->info.cname
+ && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
+ return false;
+ if( *mask->info.game
+ && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
+ return false;
+ if( *mask->info.mod
+ && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
+ return false;
+ if( *mask->info.map
+ && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
+ return false;
+ if( *mask->info.name
+ && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
+ return false;
+ return true;
+}
+
+static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
+{
+ int start, end, mid;
+
+ // FIXME: change this to be more readable (...)
+ // now check whether it passes through the masks
+ for( start = 0 ; serverlist_andmasks[start].active && start < SERVERLIST_ANDMASKCOUNT ; start++ )
+ if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
+ return;
+
+ for( start = 0 ; serverlist_ormasks[start].active && start < SERVERLIST_ORMASKCOUNT ; start++ )
+ if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
+ break;
+ if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
+ return;
+
+ if( !serverlist_viewcount ) {
+ _ServerList_ViewList_Helper_InsertBefore( 0, entry );
+ return;
+ }
+ // ok, insert it, we just need to find out where exactly:
+
+ // two special cases
+ // check whether to insert it as new first item
+ if( _ServerList_Entry_Compare( entry, serverlist_viewlist[0] ) ) {
+ _ServerList_ViewList_Helper_InsertBefore( 0, entry );
+ return;
+ } // check whether to insert it as new last item
+ else if( !_ServerList_Entry_Compare( entry, serverlist_viewlist[serverlist_viewcount - 1] ) ) {
+ _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
+ return;
+ }
+ start = 0;
+ end = serverlist_viewcount - 1;
+ while( end > start + 1 )
+ {
+ mid = (start + end) / 2;
+ // test the item that lies in the middle between start and end
+ if( _ServerList_Entry_Compare( entry, serverlist_viewlist[mid] ) )
+ // the item has to be in the upper half
+ end = mid;
+ else
+ // the item has to be in the lower half
+ start = mid;
+ }
+ _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
+}
+
+static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
+{
+ int i;
+ for( i = 0; i < serverlist_viewcount; i++ )
+ {
+ if (serverlist_viewlist[i] == entry)
+ {
+ _ServerList_ViewList_Helper_Remove(i);
+ break;
+ }
+ }
+}
+
+void ServerList_RebuildViewList(void)
+{
+ int i;
+
+ serverlist_viewcount = 0;
+ for( i = 0 ; i < serverlist_cachecount ; i++ )
+ if( serverlist_cache[i].query == SQS_QUERIED )
+ ServerList_ViewList_Insert( &serverlist_cache[i] );
+}
+
+void ServerList_ResetMasks(void)
+{
+ memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
+ memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
+}
+
+#if 0
+static void _ServerList_Test(void)
+{
+ int i;
+ for( i = 0 ; i < 1024 ; i++ ) {
+ memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
+ serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
+ dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, 128, "Black's ServerList Test %i", i );
+ serverlist_cache[serverlist_cachecount].finished = true;
+ sprintf( serverlist_cache[serverlist_cachecount].line1, "%i %s", serverlist_cache[serverlist_cachecount].info.ping, serverlist_cache[serverlist_cachecount].info.name );
+ ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
+ serverlist_cachecount++;
+ }
+}
+#endif
+
+void ServerList_QueryList(void)
+{
+ masterquerytime = realtime;
+ masterquerycount = 0;
+ masterreplycount = 0;
+ serverquerycount = 0;
+ serverreplycount = 0;
+ serverlist_cachecount = 0;
+ serverlist_viewcount = 0;
+ serverlist_consoleoutput = false;
+
+ //_ServerList_Test();
+
+ NetConn_QueryMasters();
+}
+
+// rest
+