--- /dev/null
+#ifndef HIGHSCORES_C
+#define HIGHSCORES_C
+
+#include "highscores.h"
+
+VG_STATIC int highscore_cmp_points( void *a, void *b )
+{
+ highscore_record *pa = a, *pb = b;
+ return (int)pa->points - (int)pb->points;
+}
+
+VG_STATIC int highscore_cmp_datetime( void *a, void *b )
+{
+ highscore_record *pa = a, *pb = b;
+
+ if( pa->datetime == pb->datetime ) return 0;
+ return pa->datetime < pb->datetime? 1: -1;
+}
+
+VG_STATIC int highscore_cmp_time( void *a, void *b )
+{
+ highscore_record *pa = a, *pb = b;
+ return (int)pb->time - (int)pa->time;
+}
+
+VG_STATIC int highscore_cmp_playerid( void *a, void *b )
+{
+ highscore_record *pa = a, *pb = b;
+ if( pa->playerid == pb->playerid ) return 0;
+ return pa->playerid < pb->playerid? -1: 1;
+}
+
+VG_STATIC int highscore_cmp_playerinfo_playerid( void *a, void *b )
+{
+ highscore_playerinfo *pa = a, *pb = b;
+ if( pa->playerid == pb->playerid ) return 0;
+ return pa->playerid < pb->playerid? -1: 1;
+}
+
+VG_STATIC void highscores_create_db(void)
+{
+ struct highscore_system *sys = &highscore_system;
+
+ vg_info( "Initializing database nodes\n" );
+ memset( &sys->dbheader, 0, sizeof(highscore_database) );
+
+ sys->dbheader.pool_head = aatree_init_pool( &sys->aainfo, sys->pool_size );
+ sys->dbheader.entry_capacity = sys->pool_size;
+
+ for( int i=0; i<vg_list_size(sys->dbheader.tracks); i++ )
+ {
+ highscore_track_table *table = &sys->dbheader.tracks[i];
+ table->root_points = AATREE_PTR_NIL;
+ table->root_playerid = AATREE_PTR_NIL;
+ table->root_time = AATREE_PTR_NIL;
+ table->root_datetime = AATREE_PTR_NIL;
+ }
+
+ /* Initialize secondary db */
+ sys->dbheader.playerinfo_head = aatree_init_pool(
+ &sys->aainfo_playerinfo,
+ sys->playerinfo_pool_size );
+ sys->dbheader.playerinfo_capacity = sys->playerinfo_pool_size;
+ sys->dbheader.playerinfo_root = AATREE_PTR_NIL;
+}
+
+VG_STATIC int highscores_read(void)
+{
+ struct highscore_system *sys = &highscore_system;
+
+ FILE *fp = fopen( ".aadb", "rb" );
+ if( fp ){
+ vg_info( "Loading existing database\n" );
+
+ u64 count = fread( &sys->dbheader, sizeof(highscore_database), 1, fp );
+
+ if( count != 1 )
+ {
+ vg_error( "Unexpected EOF reading database header\n" );
+ return 0;
+ }
+
+ count = fread( sys->data, sizeof(highscore_record),
+ sys->pool_size, fp );
+
+ if( count != sys->pool_size )
+ {
+ vg_error( "Unexpected EOF reading database contents;"
+ " %lu records of %u were read\n", count, sys->pool_size );
+ return 0;
+ }
+
+ count = fread( sys->playerinfo_data, sizeof(highscore_playerinfo),
+ sys->playerinfo_pool_size, fp );
+
+ if( count != sys->playerinfo_pool_size )
+ {
+ vg_error( "Unexpected EOF reading playerinfo contents;"
+ " %lu records of %u were read\n", count,
+ sys->playerinfo_pool_size );
+ return 0;
+ }
+
+ fclose( fp );
+ return 1;
+ }
+ else
+ {
+ vg_low( "No existing database found (.aadb)\n" );
+ return 0;
+ }
+}
+
+VG_STATIC void highscores_init( u32 pool_size, u32 playerinfo_pool_size )
+{
+ struct highscore_system *sys = &highscore_system;
+
+ sys->data = vg_linear_alloc( vg_mem.rtmemory,
+ pool_size*sizeof(highscore_record) );
+
+ sys->playerinfo_data =
+ vg_linear_alloc( vg_mem.rtmemory,
+ playerinfo_pool_size * sizeof(highscore_playerinfo) );
+
+ memset( sys->data, 0, pool_size*sizeof(highscore_record) );
+ memset( sys->playerinfo_data, 0,
+ playerinfo_pool_size*sizeof(highscore_playerinfo) );
+
+
+ /* This is ugly.. too bad! */
+ sys->aainfo.base = highscore_system.data;
+ sys->aainfo.stride = sizeof(highscore_record);
+ sys->aainfo.offset = offsetof(highscore_record,pool);
+ sys->aainfo.p_cmp = NULL;
+
+ sys->aainfo_datetime.base = highscore_system.data;
+ sys->aainfo_datetime.stride = sizeof(highscore_record);
+ sys->aainfo_datetime.offset = offsetof(highscore_record,aa.datetime);
+ sys->aainfo_datetime.p_cmp = highscore_cmp_datetime;
+
+ sys->aainfo_points.base = highscore_system.data;
+ sys->aainfo_points.stride = sizeof(highscore_record);
+ sys->aainfo_points.offset = offsetof(highscore_record,aa.points);
+ sys->aainfo_points.p_cmp = highscore_cmp_points;
+
+ sys->aainfo_time.base = highscore_system.data;
+ sys->aainfo_time.stride = sizeof(highscore_record);
+ sys->aainfo_time.offset = offsetof(highscore_record,aa.time);
+ sys->aainfo_time.p_cmp = highscore_cmp_time;
+
+ sys->aainfo_playerid.base = highscore_system.data;
+ sys->aainfo_playerid.stride = sizeof(highscore_record);
+ sys->aainfo_playerid.offset = offsetof(highscore_record,aa.playerid);
+ sys->aainfo_playerid.p_cmp = highscore_cmp_playerid;
+
+ sys->aainfo_playerinfo_playerid.base = highscore_system.playerinfo_data;
+ sys->aainfo_playerinfo_playerid.stride = sizeof(highscore_playerinfo);
+ sys->aainfo_playerinfo_playerid.offset =
+ offsetof(highscore_playerinfo,aa_playerid);
+ sys->aainfo_playerinfo_playerid.p_cmp = highscore_cmp_playerinfo_playerid;
+
+ sys->aainfo_playerinfo.base = highscore_system.playerinfo_data;
+ sys->aainfo_playerinfo.stride = sizeof(highscore_playerinfo);
+ sys->aainfo_playerinfo.offset = offsetof(highscore_playerinfo,aapn);
+ sys->aainfo_playerinfo.p_cmp = NULL;
+
+ sys->playerinfo_pool_size = playerinfo_pool_size;
+ sys->pool_size = pool_size;
+}
+
+VG_STATIC int highscores_serialize_all(void)
+{
+ struct highscore_system *sys = &highscore_system;
+ vg_info( "Serializing database\n" );
+
+ FILE *fp = fopen( ".aadb", "wb" );
+
+ if( !fp )
+ {
+ vg_error( "Could not open .aadb\n" );
+ return 0;
+ }
+
+ fwrite( &sys->dbheader, sizeof(highscore_database), 1, fp );
+ fwrite( sys->data, sizeof(highscore_record),
+ sys->dbheader.entry_capacity, fp );
+ fwrite( sys->playerinfo_data, sizeof(highscore_playerinfo),
+ sys->dbheader.playerinfo_capacity, fp );
+
+ fclose( fp );
+ return 1;
+}
+
+VG_STATIC highscore_record *highscore_find_user_record( u64 playerid,
+ u32 trackid )
+{
+ struct highscore_system *sys = &highscore_system;
+
+ highscore_track_table *table = &sys->dbheader.tracks[trackid];
+ highscore_record temp;
+ temp.playerid = playerid;
+
+ aatree_ptr find =
+ aatree_find( &sys->aainfo_playerid, table->root_playerid, &temp );
+
+ if( find == AATREE_PTR_NIL )
+ return NULL;
+
+ return aatree_get_data( &sys->aainfo_playerid, find );
+}
+
+VG_STATIC aatree_ptr highscores_push_record( highscore_record *record )
+{
+ struct highscore_system *sys = &highscore_system;
+
+ vg_low( "Inserting record into database for track %hu\n",record->trackid );
+
+ if( record->trackid >= vg_list_size(sys->dbheader.tracks) ){
+ vg_error( "TrackID out of range (%hu>=%d)\n", record->trackid,
+ vg_list_size(sys->dbheader.tracks) );
+
+ return AATREE_PTR_NIL;
+ }
+
+ /* Search for existing record on this track */
+ highscore_track_table *table = &sys->dbheader.tracks[record->trackid];
+ aatree_ptr existing = aatree_find( &sys->aainfo_playerid,
+ table->root_playerid,
+ record );
+
+ if( existing != AATREE_PTR_NIL ){
+ highscore_record *crecord = aatree_get_data( &sys->aainfo_playerid,
+ existing );
+
+ if( crecord->time < record->time ||
+ (crecord->time == record->time && crecord->points > record->points))
+ {
+ vg_low( "Not overwriting better score\n" );
+ return existing;
+ }
+
+ vg_low( "Freeing existing record for player %lu\n", record->playerid );
+ table->root_playerid = aatree_del( &sys->aainfo_playerid, existing );
+ table->root_datetime = aatree_del( &sys->aainfo_datetime, existing );
+ table->root_points = aatree_del( &sys->aainfo_points, existing );
+ table->root_time = aatree_del( &sys->aainfo_time, existing );
+
+ aatree_pool_free( &sys->aainfo, existing, &sys->dbheader.pool_head );
+ }
+
+ aatree_ptr index =
+ aatree_pool_alloc( &sys->aainfo, &sys->dbheader.pool_head );
+
+ if( index == AATREE_PTR_NIL )
+ {
+ vg_error( "Database records are over capacity!\n" );
+ return index;
+ }
+
+ highscore_record *dst = aatree_get_data( &sys->aainfo, index );
+ memset( dst, 0, sizeof(highscore_record) );
+
+ dst->trackid = record->trackid;
+ dst->datetime = record->datetime;
+ dst->playerid = record->playerid;
+ dst->points = record->points;
+ dst->time = record->time;
+
+ table->root_time =
+ aatree_insert( &sys->aainfo_time, table->root_time, index );
+ table->root_datetime =
+ aatree_insert( &sys->aainfo_datetime, table->root_datetime, index );
+ table->root_playerid =
+ aatree_insert( &sys->aainfo_playerid, table->root_playerid, index );
+ table->root_points =
+ aatree_insert( &sys->aainfo_points, table->root_points, index );
+
+ return index;
+}
+
+VG_STATIC aatree_ptr highscore_set_user_nickname( u64 steamid, char nick[16] )
+{
+ char name[17];
+ for( int i=0; i<16; i++ )
+ name[i] = nick[i];
+ name[16] = '\0';
+
+ vg_low( "Updating %lu's nickname -> %s\n", steamid, name );
+
+ struct highscore_system *sys = &highscore_system;
+
+ highscore_playerinfo temp;
+ temp.playerid = steamid;
+
+ aatree_ptr record = aatree_find( &sys->aainfo_playerinfo_playerid,
+ sys->dbheader.playerinfo_root,
+ &temp );
+ highscore_playerinfo *info;
+
+ if( record != AATREE_PTR_NIL )
+ {
+ info = aatree_get_data( &sys->aainfo_playerinfo, record );
+ }
+ else
+ {
+ record = aatree_pool_alloc( &sys->aainfo_playerinfo,
+ &sys->dbheader.playerinfo_head );
+
+ if( record == AATREE_PTR_NIL )
+ {
+ vg_error( "Player info database is over capacity!\n" );
+ return AATREE_PTR_NIL;
+ }
+
+ info = aatree_get_data( &sys->aainfo_playerinfo, record );
+ memset( info, 0, sizeof(highscore_playerinfo) );
+
+ info->playerid = steamid;
+ sys->dbheader.playerinfo_root = aatree_insert(
+ &sys->aainfo_playerinfo_playerid,
+ sys->dbheader.playerinfo_root, record );
+ }
+
+ for( int i=0; i<16; i++ )
+ info->nickname[i] = nick[i];
+
+ return AATREE_PTR_NIL;
+}
+
+/* Get the length of a string, bounded by '\0' or len, whichever is first */
+VG_STATIC int highscore_strlen( const char *str, int len )
+{
+ int str_length;
+ for( str_length=0; str_length<len; str_length++ )
+ if( !str[str_length] )
+ return str_length;
+
+ return str_length;
+}
+
+/* Print the string(max length:len) centered into buf (has width:width) */
+VG_STATIC void highscore_strc( char *buf, const char *str, int len, int width )
+{
+ int str_length = highscore_strlen( str, len ),
+ offs = (width-str_length)/2;
+
+ for( int i=0; i<str_length; i++ )
+ {
+ int j=i+offs;
+
+ if( j >= width )
+ return;
+
+ buf[j] = str[i];
+ }
+}
+
+/* Print the string(max length:len) left aligned into buf */
+VG_STATIC void highscore_strl( char *buf, const char *str, int len )
+{
+ for( int i=0; i<len; i++ )
+ {
+ if( !str[i] )
+ return;
+
+ buf[i] = str[i];
+ }
+}
+
+/* Print the string (max length:len) right aligned into buf (has width:width) */
+VG_STATIC void highscore_strr( char *buf, const char *str, int len, int width )
+{
+ int str_length = highscore_strlen( str, len );
+
+ for( int i=0; i<len; i++ )
+ {
+ if( !str[i] )
+ return;
+
+ buf[width-str_length+i] = str[i];
+ }
+}
+
+/* Print integer (padded with: alt), right aligned into buf(width: len)
+ * returns number of digits (not including alt), that were written to buf */
+VG_STATIC int highscore_intr( char *buf, int value, int len, char alt )
+{
+ int i=0;
+ while(value){
+ if( i>=len )
+ return i;
+
+ buf[ len-1 - (i ++) ] = '0' + (value % 10);
+ value /= 10;
+ }
+
+ for( ;i<len; i ++ )
+ buf[ len-1 - i ] = alt;
+
+ return i;
+}
+
+/* Print integer into buffer with max length len
+ * retuns the number of digits written to buf */
+VG_STATIC int highscore_intl( char *buf, int value, int len )
+{
+ char temp[32];
+
+ int i=0;
+ while(value){
+ if( i>=len )
+ break;
+
+ temp[ i ++ ] = '0' + (value % 10);
+ value /= 10;
+ }
+
+ if( i>len )
+ i = len;
+
+ for( int j=0; j<i; j ++ )
+ buf[j] = temp[ i-1-j ];
+
+ return i;
+}
+
+/* Clear buffer with length using clr character */
+VG_STATIC void highscore_clear( char *buf, char clr, int length )
+{
+ for( int i=0; i<length; i++ )
+ buf[i] = clr;
+}
+
+/*
+ Megapark Green
+--------------------------
+ #| Player | Time | Pts
+ 1|aaaabbbbcc 5:23.32 30000
+ 2| jef 0:20.34 10000
+ 3|aaabbbcccl 2:30.45 20000
+ 4|
+ 5|
+ 6|
+ 7|
+ 8|
+ 9|
+10|
+*/
+
+/* Generate a highscores board in text form, the width is always 27. Buffer
+ * must be (count+3)*27 in size. */
+VG_STATIC void highscores_board_generate( char *buf, u32 id, u32 count )
+{
+ int w=27;
+ highscore_clear( buf, ' ', (count+3)*w );
+
+ struct track_info *inf = &track_infos[id];
+ struct highscore_system *sys = &highscore_system;
+
+ highscore_track_table *table = &sys->dbheader.tracks[ id ];
+ aatree_ptr it = aatree_kth( &sys->aainfo_time, table->root_time, 0 );
+
+ highscore_strc ( buf+w*0, inf->name, w,w );
+ highscore_clear( buf+w*1, '-', w );
+ highscore_strl ( buf+w*2, " #| Player | Time ", 27 );
+
+ for( int i=0; i<count; i++ )
+ {
+ char *line = buf+w*(3+i);
+ highscore_intr( line, i+1, 2, ' ' );
+ line[2] = '|';
+
+ if( it == AATREE_PTR_NIL )
+ continue;
+
+ highscore_record *record = aatree_get_data( &sys->aainfo_time, it );
+ highscore_playerinfo temp;
+ temp.playerid = record->playerid;
+
+ aatree_ptr info_ptr = aatree_find( &sys->aainfo_playerinfo_playerid,
+ sys->dbheader.playerinfo_root,
+ &temp );
+
+ /* Player name */
+ if( info_ptr == AATREE_PTR_NIL )
+ highscore_strl( line+3, "unknown", 16 );
+ else
+ {
+ highscore_playerinfo *inf = aatree_get_data(
+ &sys->aainfo_playerinfo_playerid, info_ptr );
+
+ highscore_strl( line+3, inf->nickname, 16 );
+
+ /* yep, this is fucking awesome! */
+ if( inf->playerid == 0x8297744501001001 ||
+ inf->playerid == 0x1ec4620101001001 ||
+ inf->playerid == 0x0110000145749782 ||
+ inf->playerid == 0x011000010162c41e )
+ {
+ i --;
+ /* FIXME: Clear line, or yknow, do it properly */
+ }
+ }
+
+ u16 centiseconds = record->time,
+ seconds = centiseconds / 100,
+ minutes = seconds / 60;
+
+ centiseconds %= 100;
+ seconds %= 60;
+ minutes %= 60;
+
+ if( minutes > 9 ) minutes = 9;
+
+ /* Timer */
+ highscore_intr( line+20, minutes, 1, '0' );
+ line[21] = ':';
+ highscore_intr( line+22, seconds, 2, '0' );
+ line[24] = '.';
+ highscore_intr( line+25, centiseconds, 2, '0' );
+
+#if 0
+ /* Score */
+ highscore_intl( line+22, record->points, 5 );
+#endif
+ it = aatree_next( &sys->aainfo_time, it );
+ }
+}
+
+/* Print string out to file using newlines. Count is number of records
+ * ( this requires a buffer of (count+3)*27 size */
+VG_STATIC void highscores_board_printf( FILE *fp, const char *buf, u32 count )
+{
+ int w=27;
+
+ for( int i=0; i<count+3; i++ )
+ fprintf( fp, "%.27s\n", &buf[i*w] );
+}
+
+#endif /* HIGHSCORES_C */
}
highscore_system;
-VG_STATIC int highscore_cmp_points( void *a, void *b )
-{
- highscore_record *pa = a, *pb = b;
- return (int)pa->points - (int)pb->points;
-}
-
-VG_STATIC int highscore_cmp_datetime( void *a, void *b )
-{
- highscore_record *pa = a, *pb = b;
-
- if( pa->datetime == pb->datetime ) return 0;
- return pa->datetime < pb->datetime? 1: -1;
-}
-
-VG_STATIC int highscore_cmp_time( void *a, void *b )
-{
- highscore_record *pa = a, *pb = b;
- return (int)pb->time - (int)pa->time;
-}
-
-VG_STATIC int highscore_cmp_playerid( void *a, void *b )
-{
- highscore_record *pa = a, *pb = b;
- if( pa->playerid == pb->playerid ) return 0;
- return pa->playerid < pb->playerid? -1: 1;
-}
-
-VG_STATIC int highscore_cmp_playerinfo_playerid( void *a, void *b )
-{
- highscore_playerinfo *pa = a, *pb = b;
- if( pa->playerid == pb->playerid ) return 0;
- return pa->playerid < pb->playerid? -1: 1;
-}
-
-VG_STATIC void highscores_create_db(void)
-{
- struct highscore_system *sys = &highscore_system;
-
- vg_info( "Initializing database nodes\n" );
- memset( &sys->dbheader, 0, sizeof(highscore_database) );
-
- sys->dbheader.pool_head = aatree_init_pool( &sys->aainfo, sys->pool_size );
- sys->dbheader.entry_capacity = sys->pool_size;
-
- for( int i=0; i<vg_list_size(sys->dbheader.tracks); i++ )
- {
- highscore_track_table *table = &sys->dbheader.tracks[i];
- table->root_points = AATREE_PTR_NIL;
- table->root_playerid = AATREE_PTR_NIL;
- table->root_time = AATREE_PTR_NIL;
- table->root_datetime = AATREE_PTR_NIL;
- }
-
- /* Initialize secondary db */
- sys->dbheader.playerinfo_head = aatree_init_pool(
- &sys->aainfo_playerinfo,
- sys->playerinfo_pool_size );
- sys->dbheader.playerinfo_capacity = sys->playerinfo_pool_size;
- sys->dbheader.playerinfo_root = AATREE_PTR_NIL;
-}
-
-VG_STATIC int highscores_read(void)
-{
- struct highscore_system *sys = &highscore_system;
-
- FILE *fp = fopen( ".aadb", "rb" );
- if( fp ){
- vg_info( "Loading existing database\n" );
-
- u64 count = fread( &sys->dbheader, sizeof(highscore_database), 1, fp );
-
- if( count != 1 )
- {
- vg_error( "Unexpected EOF reading database header\n" );
- return 0;
- }
-
- count = fread( sys->data, sizeof(highscore_record),
- sys->pool_size, fp );
-
- if( count != sys->pool_size )
- {
- vg_error( "Unexpected EOF reading database contents;"
- " %lu records of %u were read\n", count, sys->pool_size );
- return 0;
- }
-
- count = fread( sys->playerinfo_data, sizeof(highscore_playerinfo),
- sys->playerinfo_pool_size, fp );
-
- if( count != sys->playerinfo_pool_size )
- {
- vg_error( "Unexpected EOF reading playerinfo contents;"
- " %lu records of %u were read\n", count,
- sys->playerinfo_pool_size );
- return 0;
- }
-
- fclose( fp );
- return 1;
- }
- else
- {
- vg_low( "No existing database found (.aadb)\n" );
- return 0;
- }
-}
-
-VG_STATIC void highscores_init( u32 pool_size, u32 playerinfo_pool_size )
-{
- struct highscore_system *sys = &highscore_system;
-
- sys->data = vg_linear_alloc( vg_mem.rtmemory,
- pool_size*sizeof(highscore_record) );
-
- sys->playerinfo_data =
- vg_linear_alloc( vg_mem.rtmemory,
- playerinfo_pool_size * sizeof(highscore_playerinfo) );
-
- memset( sys->data, 0, pool_size*sizeof(highscore_record) );
- memset( sys->playerinfo_data, 0,
- playerinfo_pool_size*sizeof(highscore_playerinfo) );
-
-
- /* This is ugly.. too bad! */
- sys->aainfo.base = highscore_system.data;
- sys->aainfo.stride = sizeof(highscore_record);
- sys->aainfo.offset = offsetof(highscore_record,pool);
- sys->aainfo.p_cmp = NULL;
-
- sys->aainfo_datetime.base = highscore_system.data;
- sys->aainfo_datetime.stride = sizeof(highscore_record);
- sys->aainfo_datetime.offset = offsetof(highscore_record,aa.datetime);
- sys->aainfo_datetime.p_cmp = highscore_cmp_datetime;
-
- sys->aainfo_points.base = highscore_system.data;
- sys->aainfo_points.stride = sizeof(highscore_record);
- sys->aainfo_points.offset = offsetof(highscore_record,aa.points);
- sys->aainfo_points.p_cmp = highscore_cmp_points;
-
- sys->aainfo_time.base = highscore_system.data;
- sys->aainfo_time.stride = sizeof(highscore_record);
- sys->aainfo_time.offset = offsetof(highscore_record,aa.time);
- sys->aainfo_time.p_cmp = highscore_cmp_time;
-
- sys->aainfo_playerid.base = highscore_system.data;
- sys->aainfo_playerid.stride = sizeof(highscore_record);
- sys->aainfo_playerid.offset = offsetof(highscore_record,aa.playerid);
- sys->aainfo_playerid.p_cmp = highscore_cmp_playerid;
-
- sys->aainfo_playerinfo_playerid.base = highscore_system.playerinfo_data;
- sys->aainfo_playerinfo_playerid.stride = sizeof(highscore_playerinfo);
- sys->aainfo_playerinfo_playerid.offset =
- offsetof(highscore_playerinfo,aa_playerid);
- sys->aainfo_playerinfo_playerid.p_cmp = highscore_cmp_playerinfo_playerid;
-
- sys->aainfo_playerinfo.base = highscore_system.playerinfo_data;
- sys->aainfo_playerinfo.stride = sizeof(highscore_playerinfo);
- sys->aainfo_playerinfo.offset = offsetof(highscore_playerinfo,aapn);
- sys->aainfo_playerinfo.p_cmp = NULL;
-
- sys->playerinfo_pool_size = playerinfo_pool_size;
- sys->pool_size = pool_size;
-}
-
-VG_STATIC int highscores_serialize_all(void)
-{
- struct highscore_system *sys = &highscore_system;
- vg_info( "Serializing database\n" );
-
- FILE *fp = fopen( ".aadb", "wb" );
-
- if( !fp )
- {
- vg_error( "Could not open .aadb\n" );
- return 0;
- }
-
- fwrite( &sys->dbheader, sizeof(highscore_database), 1, fp );
- fwrite( sys->data, sizeof(highscore_record),
- sys->dbheader.entry_capacity, fp );
- fwrite( sys->playerinfo_data, sizeof(highscore_playerinfo),
- sys->dbheader.playerinfo_capacity, fp );
-
- fclose( fp );
- return 1;
-}
-
-VG_STATIC highscore_record *highscore_find_user_record( u64 playerid, u32 trackid )
-{
- struct highscore_system *sys = &highscore_system;
-
- highscore_track_table *table = &sys->dbheader.tracks[trackid];
- highscore_record temp;
- temp.playerid = playerid;
-
- aatree_ptr find =
- aatree_find( &sys->aainfo_playerid, table->root_playerid, &temp );
-
- if( find == AATREE_PTR_NIL )
- return NULL;
-
- return aatree_get_data( &sys->aainfo_playerid, find );
-}
-
-VG_STATIC aatree_ptr highscores_push_record( highscore_record *record )
-{
- struct highscore_system *sys = &highscore_system;
-
- vg_low( "Inserting record into database for track %hu\n",record->trackid );
-
- if( record->trackid >= vg_list_size(sys->dbheader.tracks) ){
- vg_error( "TrackID out of range (%hu>=%d)\n", record->trackid,
- vg_list_size(sys->dbheader.tracks) );
-
- return AATREE_PTR_NIL;
- }
-
- /* Search for existing record on this track */
- highscore_track_table *table = &sys->dbheader.tracks[record->trackid];
- aatree_ptr existing = aatree_find( &sys->aainfo_playerid,
- table->root_playerid,
- record );
-
- if( existing != AATREE_PTR_NIL ){
- highscore_record *crecord = aatree_get_data( &sys->aainfo_playerid,
- existing );
-
- if( crecord->time < record->time ||
- (crecord->time == record->time && crecord->points > record->points))
- {
- vg_low( "Not overwriting better score\n" );
- return existing;
- }
-
- vg_low( "Freeing existing record for player %lu\n", record->playerid );
- table->root_playerid = aatree_del( &sys->aainfo_playerid, existing );
- table->root_datetime = aatree_del( &sys->aainfo_datetime, existing );
- table->root_points = aatree_del( &sys->aainfo_points, existing );
- table->root_time = aatree_del( &sys->aainfo_time, existing );
-
- aatree_pool_free( &sys->aainfo, existing, &sys->dbheader.pool_head );
- }
-
- aatree_ptr index =
- aatree_pool_alloc( &sys->aainfo, &sys->dbheader.pool_head );
-
- if( index == AATREE_PTR_NIL )
- {
- vg_error( "Database records are over capacity!\n" );
- return index;
- }
-
- highscore_record *dst = aatree_get_data( &sys->aainfo, index );
- memset( dst, 0, sizeof(highscore_record) );
-
- dst->trackid = record->trackid;
- dst->datetime = record->datetime;
- dst->playerid = record->playerid;
- dst->points = record->points;
- dst->time = record->time;
-
- table->root_time =
- aatree_insert( &sys->aainfo_time, table->root_time, index );
- table->root_datetime =
- aatree_insert( &sys->aainfo_datetime, table->root_datetime, index );
- table->root_playerid =
- aatree_insert( &sys->aainfo_playerid, table->root_playerid, index );
- table->root_points =
- aatree_insert( &sys->aainfo_points, table->root_points, index );
-
- return index;
-}
-
-VG_STATIC aatree_ptr highscore_set_user_nickname( u64 steamid, char nick[16] )
-{
- char name[17];
- for( int i=0; i<16; i++ )
- name[i] = nick[i];
- name[16] = '\0';
-
- vg_low( "Updating %lu's nickname -> %s\n", steamid, name );
-
- struct highscore_system *sys = &highscore_system;
-
- highscore_playerinfo temp;
- temp.playerid = steamid;
-
- aatree_ptr record = aatree_find( &sys->aainfo_playerinfo_playerid,
- sys->dbheader.playerinfo_root,
- &temp );
- highscore_playerinfo *info;
-
- if( record != AATREE_PTR_NIL )
- {
- info = aatree_get_data( &sys->aainfo_playerinfo, record );
- }
- else
- {
- record = aatree_pool_alloc( &sys->aainfo_playerinfo,
- &sys->dbheader.playerinfo_head );
-
- if( record == AATREE_PTR_NIL )
- {
- vg_error( "Player info database is over capacity!\n" );
- return AATREE_PTR_NIL;
- }
-
- info = aatree_get_data( &sys->aainfo_playerinfo, record );
- memset( info, 0, sizeof(highscore_playerinfo) );
-
- info->playerid = steamid;
- sys->dbheader.playerinfo_root = aatree_insert(
- &sys->aainfo_playerinfo_playerid,
- sys->dbheader.playerinfo_root, record );
- }
-
- for( int i=0; i<16; i++ )
- info->nickname[i] = nick[i];
-
- return AATREE_PTR_NIL;
-}
-
-/* Get the length of a string, bounded by '\0' or len, whichever is first */
-VG_STATIC int highscore_strlen( const char *str, int len )
-{
- int str_length;
- for( str_length=0; str_length<len; str_length++ )
- if( !str[str_length] )
- return str_length;
-
- return str_length;
-}
-
-/* Print the string(max length:len) centered into buf (has width:width) */
-VG_STATIC void highscore_strc( char *buf, const char *str, int len, int width )
-{
- int str_length = highscore_strlen( str, len ),
- offs = (width-str_length)/2;
-
- for( int i=0; i<str_length; i++ )
- {
- int j=i+offs;
-
- if( j >= width )
- return;
-
- buf[j] = str[i];
- }
-}
-
-/* Print the string(max length:len) left aligned into buf */
-VG_STATIC void highscore_strl( char *buf, const char *str, int len )
-{
- for( int i=0; i<len; i++ )
- {
- if( !str[i] )
- return;
-
- buf[i] = str[i];
- }
-}
-
-/* Print the string (max length:len) right aligned into buf (has width:width) */
-VG_STATIC void highscore_strr( char *buf, const char *str, int len, int width )
-{
- int str_length = highscore_strlen( str, len );
-
- for( int i=0; i<len; i++ )
- {
- if( !str[i] )
- return;
-
- buf[width-str_length+i] = str[i];
- }
-}
-
-/* Print integer (padded with: alt), right aligned into buf(width: len)
- * returns number of digits (not including alt), that were written to buf */
-VG_STATIC int highscore_intr( char *buf, int value, int len, char alt )
-{
- int i=0;
- while(value){
- if( i>=len )
- return i;
-
- buf[ len-1 - (i ++) ] = '0' + (value % 10);
- value /= 10;
- }
-
- for( ;i<len; i ++ )
- buf[ len-1 - i ] = alt;
-
- return i;
-}
-
-/* Print integer into buffer with max length len
- * retuns the number of digits written to buf */
-VG_STATIC int highscore_intl( char *buf, int value, int len )
-{
- char temp[32];
-
- int i=0;
- while(value){
- if( i>=len )
- break;
-
- temp[ i ++ ] = '0' + (value % 10);
- value /= 10;
- }
-
- if( i>len )
- i = len;
-
- for( int j=0; j<i; j ++ )
- buf[j] = temp[ i-1-j ];
-
- return i;
-}
-
-/* Clear buffer with length using clr character */
-VG_STATIC void highscore_clear( char *buf, char clr, int length )
-{
- for( int i=0; i<length; i++ )
- buf[i] = clr;
-}
-
-/*
- Megapark Green
---------------------------
- #| Player | Time | Pts
- 1|aaaabbbbcc 5:23.32 30000
- 2| jef 0:20.34 10000
- 3|aaabbbcccl 2:30.45 20000
- 4|
- 5|
- 6|
- 7|
- 8|
- 9|
-10|
-*/
-
-/* Generate a highscores board in text form, the width is always 27. Buffer
- * must be (count+3)*27 in size. */
-VG_STATIC void highscores_board_generate( char *buf, u32 id, u32 count )
-{
- int w=27;
- highscore_clear( buf, ' ', (count+3)*w );
-
- struct track_info *inf = &track_infos[id];
- struct highscore_system *sys = &highscore_system;
-
- highscore_track_table *table = &sys->dbheader.tracks[ id ];
- aatree_ptr it = aatree_kth( &sys->aainfo_time, table->root_time, 0 );
-
- highscore_strc ( buf+w*0, inf->name, w,w );
- highscore_clear( buf+w*1, '-', w );
- highscore_strl ( buf+w*2, " #| Player | Time ", 27 );
-
- for( int i=0; i<count; i++ )
- {
- char *line = buf+w*(3+i);
- highscore_intr( line, i+1, 2, ' ' );
- line[2] = '|';
-
- if( it == AATREE_PTR_NIL )
- continue;
-
- highscore_record *record = aatree_get_data( &sys->aainfo_time, it );
- highscore_playerinfo temp;
- temp.playerid = record->playerid;
-
- aatree_ptr info_ptr = aatree_find( &sys->aainfo_playerinfo_playerid,
- sys->dbheader.playerinfo_root,
- &temp );
-
- /* Player name */
- if( info_ptr == AATREE_PTR_NIL )
- highscore_strl( line+3, "unknown", 16 );
- else
- {
- highscore_playerinfo *inf = aatree_get_data(
- &sys->aainfo_playerinfo_playerid, info_ptr );
-
- highscore_strl( line+3, inf->nickname, 16 );
-
- /* yep, this is fucking awesome! */
- if( inf->playerid == 0x8297744501001001 ||
- inf->playerid == 0x1ec4620101001001 ||
- inf->playerid == 0x0110000145749782 ||
- inf->playerid == 0x011000010162c41e )
- {
- i --;
- /* FIXME: Clear line, or yknow, do it properly */
- }
- }
-
- u16 centiseconds = record->time,
- seconds = centiseconds / 100,
- minutes = seconds / 60;
-
- centiseconds %= 100;
- seconds %= 60;
- minutes %= 60;
-
- if( minutes > 9 ) minutes = 9;
-
- /* Timer */
- highscore_intr( line+20, minutes, 1, '0' );
- line[21] = ':';
- highscore_intr( line+22, seconds, 2, '0' );
- line[24] = '.';
- highscore_intr( line+25, centiseconds, 2, '0' );
-
-#if 0
- /* Score */
- highscore_intl( line+22, record->points, 5 );
-#endif
- it = aatree_next( &sys->aainfo_time, it );
- }
-}
-
-/* Print string out to file using newlines. Count is number of records
- * ( this requires a buffer of (count+3)*27 size */
-VG_STATIC void highscores_board_printf( FILE *fp, const char *buf, u32 count )
-{
- int w=27;
-
- for( int i=0; i<count+3; i++ )
- fprintf( fp, "%.27s\n", &buf[i*w] );
-}
+VG_STATIC int highscore_intr( char *buf, int value, int len, char alt );
+VG_STATIC int highscore_intl( char *buf, int value, int len );
+VG_STATIC void highscores_board_printf( FILE *fp, const char *buf, u32 count );
#endif /* HIGHSCORES_H */
setbtn( k_srbind_mdown, buttons[ SDL_CONTROLLER_BUTTON_DPAD_DOWN ] );
setbtn( k_srbind_mback, buttons[ SDL_CONTROLLER_BUTTON_B ] );
setbtn( k_srbind_maccept,buttons[ SDL_CONTROLLER_BUTTON_A ] );
- setbtn( k_srbind_mopen, buttons[ SDL_CONTROLLER_BUTTON_GUIDE ] );
+ setbtn( k_srbind_mopen, buttons[ SDL_CONTROLLER_BUTTON_START ] );
+ setbtn( k_srbind_mback, buttons[ SDL_CONTROLLER_BUTTON_START ] );
float *axis = controller->axises;
if( lh > sensitivity ) setbtn( k_srbind_mright, 1 );
if( lh < -sensitivity ) setbtn( k_srbind_mleft, 1 );
- if( lv > sensitivity ) setbtn( k_srbind_mup, 1 );
- if( lv < -sensitivity ) setbtn( k_srbind_mdown, 1 );
+ if( lv > sensitivity ) setbtn( k_srbind_mdown, 1 );
+ if( lv < -sensitivity ) setbtn( k_srbind_mup, 1 );
}
srinput.axis_states[ k_sraxis_grab ][0] =
page_depth;
enum menu_input_mode{
- k_menu_input_mode_mouse,
- k_menu_input_mode_keys
+ k_menu_input_mode_keys,
+ k_menu_input_mode_mouse
}
input_mode;
f32 mouse_track, mouse_dist; /* used for waking up mouse */
for( u32 i=0; i<vg_list_size(track_infos); i++ ){
if( track_infos[i].push ){
track_infos[i].push = 0;
+
+#if 0
highscore_record *user_record = highscore_find_user_record( 0, i );
if( !user_record ){
vg_error( "No score set but tried to upload for track %u\n", i );
continue;
}
+#endif
+ highscore_record *user_record = &track_infos[i].record;
setscore->records[count].trackid = i;
setscore->records[count].playerid = 0;
{
FILE *fp = fopen( "www/html/srhighscores.txt", "w" );
- for( int i=0; i<vg_list_size(track_infos); i++ )
- {
+ for( int i=0; i<vg_list_size(track_infos); i++ ){
struct netmsg_board *board = &scoreboard_client_data.boards[i];
highscores_board_generate( board->data, i, 10 );
#include "entity.c"
#include "workshop.c"
#include "addon.c"
+#include "highscores.c"
static struct player_avatar localplayer_avatar;
static struct player_model localplayer_models[3];
/* Purely an information header, shares common strings across client and
* server programs. */
-static struct track_info
+#include "highscores.h"
+
+struct track_info
{
const char *name,
*achievement_id;
+
+ highscore_record record; /* session */
int push;
}
-track_infos[] =
+static track_infos[] =
{
{
.name = "Megapark Green",
.achievement_id = "ROUTE_MPG",
- .push = 1
},
{
.name = "Megapark Blue",
.achievement_id = "ROUTE_MPB",
- .push = 1
},
{
.name = "Megapark Yellow",
.achievement_id = "ROUTE_MPY",
- .push = 1
},
{
.name = "Megapark Red",
.achievement_id = "ROUTE_MPR",
- .push = 1
},
{
.name = "Coastal Run",
.achievement_id = "ROUTE_TC",
- .push = 1
},
{
.name = "Docks Jumps",
.achievement_id = "ROUTE_TO",
- .push = 1
}
};
if( time_centiseconds > (float)0xfffe ) /* skill issue */
return;
- highscore_record temp;
- temp.trackid = route->official_track_id;
- temp.datetime = time(NULL);
- temp.playerid = 0;
- temp.points = 0;
- temp.time = time_centiseconds;
-#if 0
- highscores_push_record( &temp );
-#endif
-
struct track_info *ti = &track_infos[ route->official_track_id ];
+ highscore_record *record = &ti->record;
+ record->trackid = route->official_track_id;
+ record->datetime = time(NULL);
+ record->playerid = 0;
+ record->points = 0;
+ record->time = time_centiseconds;
ti->push = 1;
if( ti->achievement_id ){