4 #include "highscores.h"
6 static int highscore_cmp_points( void *a
, void *b
)
8 highscore_record
*pa
= a
, *pb
= b
;
9 return (int)pa
->points
- (int)pb
->points
;
12 static int highscore_cmp_datetime( void *a
, void *b
)
14 highscore_record
*pa
= a
, *pb
= b
;
16 if( pa
->datetime
== pb
->datetime
) return 0;
17 return pa
->datetime
< pb
->datetime
? 1: -1;
20 static int highscore_cmp_time( void *a
, void *b
)
22 highscore_record
*pa
= a
, *pb
= b
;
23 return (int)pb
->time
- (int)pa
->time
;
26 static int highscore_cmp_playerid( void *a
, void *b
)
28 highscore_record
*pa
= a
, *pb
= b
;
29 if( pa
->playerid
== pb
->playerid
) return 0;
30 return pa
->playerid
< pb
->playerid
? -1: 1;
33 static int highscore_cmp_playerinfo_playerid( void *a
, void *b
)
35 highscore_playerinfo
*pa
= a
, *pb
= b
;
36 if( pa
->playerid
== pb
->playerid
) return 0;
37 return pa
->playerid
< pb
->playerid
? -1: 1;
40 static void highscores_create_db(void)
42 struct highscore_system
*sys
= &highscore_system
;
44 vg_info( "Initializing database nodes\n" );
45 memset( &sys
->dbheader
, 0, sizeof(highscore_database
) );
47 sys
->dbheader
.pool_head
= aatree_init_pool( &sys
->aainfo
, sys
->pool_size
);
48 sys
->dbheader
.entry_capacity
= sys
->pool_size
;
50 for( int i
=0; i
<vg_list_size(sys
->dbheader
.tracks
); i
++ )
52 highscore_track_table
*table
= &sys
->dbheader
.tracks
[i
];
53 table
->root_points
= AATREE_PTR_NIL
;
54 table
->root_playerid
= AATREE_PTR_NIL
;
55 table
->root_time
= AATREE_PTR_NIL
;
56 table
->root_datetime
= AATREE_PTR_NIL
;
59 /* Initialize secondary db */
60 sys
->dbheader
.playerinfo_head
= aatree_init_pool(
61 &sys
->aainfo_playerinfo
,
62 sys
->playerinfo_pool_size
);
63 sys
->dbheader
.playerinfo_capacity
= sys
->playerinfo_pool_size
;
64 sys
->dbheader
.playerinfo_root
= AATREE_PTR_NIL
;
67 static int highscores_read( const char *path
){
68 struct highscore_system
*sys
= &highscore_system
;
73 FILE *fp
= fopen( path
, "rb" );
75 vg_info( "Loading existing database\n" );
77 u64 count
= fread( &sys
->dbheader
, sizeof(highscore_database
), 1, fp
);
81 vg_error( "Unexpected EOF reading database header\n" );
85 count
= fread( sys
->data
, sizeof(highscore_record
),
88 if( count
!= sys
->pool_size
)
90 vg_error( "Unexpected EOF reading database contents;"
91 " %lu records of %u were read\n", count
, sys
->pool_size
);
95 count
= fread( sys
->playerinfo_data
, sizeof(highscore_playerinfo
),
96 sys
->playerinfo_pool_size
, fp
);
98 if( count
!= sys
->playerinfo_pool_size
)
100 vg_error( "Unexpected EOF reading playerinfo contents;"
101 " %lu records of %u were read\n", count
,
102 sys
->playerinfo_pool_size
);
111 vg_low( "No existing database found (%s)\n", path
);
116 static void highscores_init( u32 pool_size
, u32 playerinfo_pool_size
)
118 struct highscore_system
*sys
= &highscore_system
;
120 sys
->data
= vg_linear_alloc( vg_mem
.rtmemory
,
121 pool_size
*sizeof(highscore_record
) );
123 sys
->playerinfo_data
=
124 vg_linear_alloc( vg_mem
.rtmemory
,
125 playerinfo_pool_size
* sizeof(highscore_playerinfo
) );
127 memset( sys
->data
, 0, pool_size
*sizeof(highscore_record
) );
128 memset( sys
->playerinfo_data
, 0,
129 playerinfo_pool_size
*sizeof(highscore_playerinfo
) );
132 /* This is ugly.. too bad! */
133 sys
->aainfo
.base
= highscore_system
.data
;
134 sys
->aainfo
.stride
= sizeof(highscore_record
);
135 sys
->aainfo
.offset
= offsetof(highscore_record
,pool
);
136 sys
->aainfo
.p_cmp
= NULL
;
138 sys
->aainfo_datetime
.base
= highscore_system
.data
;
139 sys
->aainfo_datetime
.stride
= sizeof(highscore_record
);
140 sys
->aainfo_datetime
.offset
= offsetof(highscore_record
,aa
.datetime
);
141 sys
->aainfo_datetime
.p_cmp
= highscore_cmp_datetime
;
143 sys
->aainfo_points
.base
= highscore_system
.data
;
144 sys
->aainfo_points
.stride
= sizeof(highscore_record
);
145 sys
->aainfo_points
.offset
= offsetof(highscore_record
,aa
.points
);
146 sys
->aainfo_points
.p_cmp
= highscore_cmp_points
;
148 sys
->aainfo_time
.base
= highscore_system
.data
;
149 sys
->aainfo_time
.stride
= sizeof(highscore_record
);
150 sys
->aainfo_time
.offset
= offsetof(highscore_record
,aa
.time
);
151 sys
->aainfo_time
.p_cmp
= highscore_cmp_time
;
153 sys
->aainfo_playerid
.base
= highscore_system
.data
;
154 sys
->aainfo_playerid
.stride
= sizeof(highscore_record
);
155 sys
->aainfo_playerid
.offset
= offsetof(highscore_record
,aa
.playerid
);
156 sys
->aainfo_playerid
.p_cmp
= highscore_cmp_playerid
;
158 sys
->aainfo_playerinfo_playerid
.base
= highscore_system
.playerinfo_data
;
159 sys
->aainfo_playerinfo_playerid
.stride
= sizeof(highscore_playerinfo
);
160 sys
->aainfo_playerinfo_playerid
.offset
=
161 offsetof(highscore_playerinfo
,aa_playerid
);
162 sys
->aainfo_playerinfo_playerid
.p_cmp
= highscore_cmp_playerinfo_playerid
;
164 /* TODO: Is this even useable? */
165 sys
->aainfo_playerinfo
.base
= highscore_system
.playerinfo_data
;
166 sys
->aainfo_playerinfo
.stride
= sizeof(highscore_playerinfo
);
167 sys
->aainfo_playerinfo
.offset
= offsetof(highscore_playerinfo
,aapn
);
168 sys
->aainfo_playerinfo
.p_cmp
= NULL
;
170 sys
->playerinfo_pool_size
= playerinfo_pool_size
;
171 sys
->pool_size
= pool_size
;
174 static int highscores_serialize_all(void)
176 struct highscore_system
*sys
= &highscore_system
;
177 vg_info( "Serializing database\n" );
179 FILE *fp
= fopen( ".aadb", "wb" );
183 vg_error( "Could not open .aadb\n" );
187 fwrite( &sys
->dbheader
, sizeof(highscore_database
), 1, fp
);
188 fwrite( sys
->data
, sizeof(highscore_record
),
189 sys
->dbheader
.entry_capacity
, fp
);
190 fwrite( sys
->playerinfo_data
, sizeof(highscore_playerinfo
),
191 sys
->dbheader
.playerinfo_capacity
, fp
);
197 static highscore_record
*highscore_find_user_record( u64 playerid
,
200 struct highscore_system
*sys
= &highscore_system
;
202 highscore_track_table
*table
= &sys
->dbheader
.tracks
[trackid
];
203 highscore_record temp
;
204 temp
.playerid
= playerid
;
207 aatree_find( &sys
->aainfo_playerid
, table
->root_playerid
, &temp
);
209 if( find
== AATREE_PTR_NIL
)
212 return aatree_get_data( &sys
->aainfo_playerid
, find
);
215 static aatree_ptr
highscores_push_record( highscore_record
*record
)
217 struct highscore_system
*sys
= &highscore_system
;
219 vg_low( "Inserting record into database for track %hu\n",record
->trackid
);
221 if( record
->trackid
>= vg_list_size(sys
->dbheader
.tracks
) ){
222 vg_error( "TrackID out of range (%hu>=%d)\n", record
->trackid
,
223 vg_list_size(sys
->dbheader
.tracks
) );
225 return AATREE_PTR_NIL
;
228 /* Search for existing record on this track */
229 highscore_track_table
*table
= &sys
->dbheader
.tracks
[record
->trackid
];
230 aatree_ptr existing
= aatree_find( &sys
->aainfo_playerid
,
231 table
->root_playerid
,
234 if( existing
!= AATREE_PTR_NIL
){
235 highscore_record
*crecord
= aatree_get_data( &sys
->aainfo_playerid
,
238 if( crecord
->time
< record
->time
||
239 (crecord
->time
== record
->time
&& crecord
->points
> record
->points
))
241 vg_low( "Not overwriting better score\n" );
245 vg_low( "Freeing existing record for player %lu\n", record
->playerid
);
246 table
->root_playerid
= aatree_del( &sys
->aainfo_playerid
, existing
);
247 table
->root_datetime
= aatree_del( &sys
->aainfo_datetime
, existing
);
248 table
->root_points
= aatree_del( &sys
->aainfo_points
, existing
);
249 table
->root_time
= aatree_del( &sys
->aainfo_time
, existing
);
251 aatree_pool_free( &sys
->aainfo
, existing
, &sys
->dbheader
.pool_head
);
255 aatree_pool_alloc( &sys
->aainfo
, &sys
->dbheader
.pool_head
);
257 if( index
== AATREE_PTR_NIL
)
259 vg_error( "Database records are over capacity!\n" );
263 highscore_record
*dst
= aatree_get_data( &sys
->aainfo
, index
);
264 memset( dst
, 0, sizeof(highscore_record
) );
266 dst
->trackid
= record
->trackid
;
267 dst
->datetime
= record
->datetime
;
268 dst
->playerid
= record
->playerid
;
269 dst
->points
= record
->points
;
270 dst
->time
= record
->time
;
273 aatree_insert( &sys
->aainfo_time
, table
->root_time
, index
);
274 table
->root_datetime
=
275 aatree_insert( &sys
->aainfo_datetime
, table
->root_datetime
, index
);
276 table
->root_playerid
=
277 aatree_insert( &sys
->aainfo_playerid
, table
->root_playerid
, index
);
279 aatree_insert( &sys
->aainfo_points
, table
->root_points
, index
);
284 static aatree_ptr
highscore_set_user_nickname( u64 steamid
, char nick
[16] )
287 for( int i
=0; i
<16; i
++ )
291 vg_low( "Updating %lu's nickname -> %s\n", steamid
, name
);
293 struct highscore_system
*sys
= &highscore_system
;
295 highscore_playerinfo temp
;
296 temp
.playerid
= steamid
;
298 aatree_ptr record
= aatree_find( &sys
->aainfo_playerinfo_playerid
,
299 sys
->dbheader
.playerinfo_root
,
301 highscore_playerinfo
*info
;
303 if( record
!= AATREE_PTR_NIL
)
305 info
= aatree_get_data( &sys
->aainfo_playerinfo
, record
);
309 record
= aatree_pool_alloc( &sys
->aainfo_playerinfo
,
310 &sys
->dbheader
.playerinfo_head
);
312 if( record
== AATREE_PTR_NIL
)
314 vg_error( "Player info database is over capacity!\n" );
315 return AATREE_PTR_NIL
;
318 info
= aatree_get_data( &sys
->aainfo_playerinfo
, record
);
319 memset( info
, 0, sizeof(highscore_playerinfo
) );
321 info
->playerid
= steamid
;
322 sys
->dbheader
.playerinfo_root
= aatree_insert(
323 &sys
->aainfo_playerinfo_playerid
,
324 sys
->dbheader
.playerinfo_root
, record
);
327 for( int i
=0; i
<16; i
++ )
328 info
->nickname
[i
] = nick
[i
];
330 return AATREE_PTR_NIL
;
333 /* Get the length of a string, bounded by '\0' or len, whichever is first */
334 static int highscore_strlen( const char *str
, int len
)
337 for( str_length
=0; str_length
<len
; str_length
++ )
338 if( !str
[str_length
] )
344 /* Print the string(max length:len) centered into buf (has width:width) */
345 static void highscore_strc( char *buf
, const char *str
, int len
, int width
)
347 int str_length
= highscore_strlen( str
, len
),
348 offs
= (width
-str_length
)/2;
350 for( int i
=0; i
<str_length
; i
++ )
361 /* Print the string(max length:len) left aligned into buf */
362 static void highscore_strl( char *buf
, const char *str
, int len
)
364 for( int i
=0; i
<len
; i
++ )
373 /* Print the string (max length:len) right aligned into buf (has width:width) */
374 static void highscore_strr( char *buf
, const char *str
, int len
, int width
)
376 int str_length
= highscore_strlen( str
, len
);
378 for( int i
=0; i
<len
; i
++ )
383 buf
[width
-str_length
+i
] = str
[i
];
387 /* Print integer (padded with: alt), right aligned into buf(width: len)
388 * returns number of digits (not including alt), that were written to buf */
389 static int highscore_intr( char *buf
, int value
, int len
, char alt
)
396 buf
[ len
-1 - (i
++) ] = '0' + (value
% 10);
401 buf
[ len
-1 - i
] = alt
;
406 /* Print integer into buffer with max length len
407 * retuns the number of digits written to buf */
408 static int highscore_intl( char *buf
, int value
, int len
){
416 temp
[ i
++ ] = '0' + (value
% 10);
423 for( int j
=0; j
<i
; j
++ )
424 buf
[j
] = temp
[ i
-1-j
];
434 /* Clear buffer with length using clr character */
435 static void highscore_clear( char *buf
, char clr
, int length
)
437 for( int i
=0; i
<length
; i
++ )
443 --------------------------
444 #| Player | Time | Pts
445 1|aaaabbbbcc 5:23.32 30000
447 3|aaabbbcccl 2:30.45 20000
457 /* Generate a highscores board in text form, the width is always 27. Buffer
458 * must be (count+3)*27 in size. */
459 static void highscores_board_generate( char *buf
, u32 id
, u32 count
)
462 highscore_clear( buf
, ' ', (count
+3)*w
);
464 struct track_info
*inf
= &track_infos
[id
];
465 struct highscore_system
*sys
= &highscore_system
;
467 highscore_track_table
*table
= &sys
->dbheader
.tracks
[ id
];
468 aatree_ptr it
= aatree_kth( &sys
->aainfo_time
, table
->root_time
, 0 );
470 highscore_strc ( buf
+w
*0, inf
->name
, w
,w
);
471 highscore_clear( buf
+w
*1, '-', w
);
472 highscore_strl ( buf
+w
*2, " #| Player | Time ", 27 );
474 for( int i
=0; i
<count
; i
++ )
476 char *line
= buf
+w
*(3+i
);
477 highscore_intr( line
, i
+1, 2, ' ' );
480 if( it
== AATREE_PTR_NIL
)
483 highscore_record
*record
= aatree_get_data( &sys
->aainfo_time
, it
);
484 highscore_playerinfo temp
;
485 temp
.playerid
= record
->playerid
;
487 aatree_ptr info_ptr
= aatree_find( &sys
->aainfo_playerinfo_playerid
,
488 sys
->dbheader
.playerinfo_root
,
492 if( info_ptr
== AATREE_PTR_NIL
)
493 highscore_strl( line
+3, "unknown", 16 );
496 highscore_playerinfo
*inf
= aatree_get_data(
497 &sys
->aainfo_playerinfo_playerid
, info_ptr
);
499 highscore_strl( line
+3, inf
->nickname
, 16 );
501 /* yep, this is fucking awesome! */
502 if( inf
->playerid
== 0x8297744501001001 ||
503 inf
->playerid
== 0x1ec4620101001001 ||
504 inf
->playerid
== 0x0110000145749782 ||
505 inf
->playerid
== 0x011000010162c41e )
508 /* FIXME: Clear line, or yknow, do it properly */
512 u16 centiseconds
= record
->time
,
513 seconds
= centiseconds
/ 100,
514 minutes
= seconds
/ 60;
520 if( minutes
> 9 ) minutes
= 9;
523 highscore_intr( line
+20, minutes
, 1, '0' );
525 highscore_intr( line
+22, seconds
, 2, '0' );
527 highscore_intr( line
+25, centiseconds
, 2, '0' );
531 highscore_intl( line
+22, record
->points
, 5 );
533 it
= aatree_next( &sys
->aainfo_time
, it
);
537 /* Print string out to file using newlines. Count is number of records
538 * ( this requires a buffer of (count+3)*27 size */
539 static void highscores_board_printf( FILE *fp
, const char *buf
, u32 count
)
543 for( int i
=0; i
<count
+3; i
++ )
544 fprintf( fp
, "%.27s\n", &buf
[i
*w
] );
547 #endif /* HIGHSCORES_C */