source: mainline/uspace/app/tetris/scores.c@ fc0110d

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fc0110d was 6eb2e96, checked in by Jiri Svoboda <jirik.svoboda@…>, 16 years ago

str_cpy() and str_ncpy() in userspace. Nuke strcpy() and strncpy().

  • Property mode set to 100644
File size: 16.2 KB
Line 
1/* $OpenBSD: scores.c,v 1.11 2006/04/20 03:25:36 ray Exp $ */
2/* $NetBSD: scores.c,v 1.2 1995/04/22 07:42:38 cgd Exp $ */
3
4/*-
5 * Copyright (c) 1992, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Chris Torek and Darren F. Provine.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 * @(#)scores.c 8.1 (Berkeley) 5/31/93
36 */
37
38/** @addtogroup tetris
39 * @{
40 */
41/** @file
42 */
43
44/*
45 * Score code for Tetris, by Darren Provine (kilroy@gboro.glassboro.edu)
46 * modified 22 January 1992, to limit the number of entries any one
47 * person has.
48 *
49 * Major whacks since then.
50 */
51#include <errno.h>
52/* #include <err.h> */
53/* #include <fcntl.h> */
54/* #include <pwd.h> */
55#include <stdio.h>
56/* #include <stdlib.h> */
57#include <string.h>
58#include <kbd/kbd.h>
59#include <kbd/keycode.h>
60#include <stdlib.h>
61/* #include <time.h> */
62/* #include <term.h> */
63/* #include <unistd.h> */
64/* #include <sys/param.h> */
65/* #include <sys/stat.h> */
66/* #include <sys/types.h> */
67
68#include "pathnames.h"
69#include "screen.h"
70#include "tetris.h"
71#include "scores.h"
72
73/*
74 * Within this code, we can hang onto one extra "high score", leaving
75 * room for our current score (whether or not it is high).
76 *
77 * We also sometimes keep tabs on the "highest" score on each level.
78 * As long as the scores are kept sorted, this is simply the first one at
79 * that level.
80 */
81#define NUMSPOTS (MAXHISCORES + 1)
82#define NLEVELS (MAXLEVEL + 1)
83
84/* static time_t now; */
85/* static int nscores; */
86/* static int gotscores; */
87/* static struct highscore scores[NUMSPOTS]; */
88static struct highscore scores[NUMSPOTS];
89
90/* static int checkscores(struct highscore *, int); */
91/* static int cmpscores(const void *, const void *); */
92/* static void getscores(FILE **); */
93/* static void printem(int, int, struct highscore *, int, const char *); */
94/* static char *thisuser(void); */
95
96void showscores(int firstgame)
97{
98 int i;
99
100 clear_screen();
101 moveto(10, 0);
102 printf("\tRank \tLevel \tName\t points\n");
103 printf("\t========================================================\n");
104 for (i = 0; i < NUMSPOTS - 1; i++) {
105 printf("\t%6d %6d %-16s %20d\n", i+1, scores[i].hs_level, scores[i].hs_name, scores[i].hs_score);
106 }
107 if (!firstgame) {
108 printf("\t========================================================\n");
109 printf("\t Last %6d %-16s %20d\n", scores[NUMSPOTS - 1].hs_level, scores[NUMSPOTS - 1].hs_name, scores[NUMSPOTS - 1].hs_score);
110 }
111 printf("\n\n\n\n\tPress any key to return to main menu.");
112 getchar();
113}
114
115/** Copy from hiscore table score with index src to dest
116 *
117 */
118static void copyhiscore(int dest, int src)
119{
120 str_cpy(scores[dest].hs_name, STR_BOUNDS(MAXLOGNAME) + 1,
121 scores[src].hs_name);
122 scores[dest].hs_score = scores[src].hs_score;
123 scores[dest].hs_level = scores[src].hs_level;
124}
125
126void insertscore(int score, int level)
127{
128 int i,j;
129 size_t off;
130 kbd_event_t ev;
131
132 clear_screen();
133 moveto(10 , 10);
134 puts("Insert your name: ");
135 str_cpy(scores[NUMSPOTS - 1].hs_name, STR_BOUNDS(MAXLOGNAME) + 1,
136 "Player");
137 i = 6; off = 6;
138
139 moveto(10 , 28);
140 printf("%s%.*s",scores[NUMSPOTS - 1].hs_name,MAXLOGNAME-i,"........................................");
141
142 while (1) {
143 fflush(stdout);
144 if (kbd_get_event(&ev) != EOK)
145 exit(1);
146
147 if (ev.type == KE_RELEASE)
148 continue;
149
150 if (ev.key == KC_ENTER || ev.key == KC_NENTER)
151 break;
152
153 if (ev.key == KC_BACKSPACE) {
154 if (i > 0) {
155 wchar_t uc;
156
157 --i;
158 while (off > 0) {
159 --off;
160 size_t otmp = off;
161 uc = str_decode(scores[NUMSPOTS - 1].hs_name,
162 &otmp, STR_BOUNDS(MAXLOGNAME) + 1);
163 if (uc != U_SPECIAL)
164 break;
165 }
166
167 scores[NUMSPOTS - 1].hs_name[off] = '\0';
168 }
169 } else if (ev.c != '\0') {
170 if (i < (MAXLOGNAME - 1)) {
171 if (chr_encode(ev.c, scores[NUMSPOTS - 1].hs_name,
172 &off, STR_BOUNDS(MAXLOGNAME) + 1) == EOK) {
173 ++i;
174 }
175 scores[NUMSPOTS - 1].hs_name[off] = '\0';
176 }
177 }
178
179 moveto(10 , 28);
180 printf("%s%.*s",scores[NUMSPOTS - 1].hs_name,MAXLOGNAME-i,"........................................");
181 }
182
183 scores[NUMSPOTS - 1].hs_score = score;
184 scores[NUMSPOTS - 1].hs_level = level;
185
186 i = NUMSPOTS-1;
187 while ((i > 0) && (scores[i - 1].hs_score < score))
188 i--;
189
190 for (j = NUMSPOTS - 2; j > i; j--) {
191 copyhiscore(j,j-1);
192 }
193 copyhiscore(i, NUMSPOTS - 1);
194}
195
196void initscores(void)
197{
198 int i;
199 for(i = 0; i < NUMSPOTS; i++) {
200 str_cpy(scores[i].hs_name, STR_BOUNDS(MAXLOGNAME) + 1, "HelenOS Team");
201 scores[i].hs_score = (NUMSPOTS - i) * 200;
202 scores[i].hs_level = (i + 1 > MAXLEVEL?MAXLEVEL:i + 1);
203 }
204}
205
206/*
207 * Read the score file. Can be called from savescore (before showscores)
208 * or showscores (if savescore will not be called). If the given pointer
209 * is not NULL, sets *fpp to an open file pointer that corresponds to a
210 * read/write score file that is locked with LOCK_EX. Otherwise, the
211 * file is locked with LOCK_SH for the read and closed before return.
212 *
213 * Note, we assume closing the stdio file releases the lock.
214 */
215/* static void */
216/* getscores(FILE **fpp) */
217/* { */
218/* int sd, mint, lck, mask, i; */
219/* char *mstr, *human; */
220/* FILE *sf; */
221
222/* if (fpp != NULL) { */
223/* mint = O_RDWR | O_CREAT; */
224/* mstr = "r+"; */
225/* human = "read/write"; */
226/* lck = LOCK_EX; */
227/* } else { */
228/* mint = O_RDONLY; */
229/* mstr = "r"; */
230/* human = "reading"; */
231/* lck = LOCK_SH; */
232/* } */
233/* setegid(egid); */
234/* mask = umask(S_IWOTH); */
235/* sd = open(_PATH_SCOREFILE, mint, 0666); */
236/* (void)umask(mask); */
237/* setegid(gid); */
238/* if (sd < 0) { */
239/* if (fpp == NULL) { */
240/* nscores = 0; */
241/* return; */
242/* } */
243/* err(1, "cannot open %s for %s", _PATH_SCOREFILE, human); */
244/* } */
245/* setegid(egid); */
246/* if ((sf = fdopen(sd, mstr)) == NULL) */
247/* err(1, "cannot fdopen %s for %s", _PATH_SCOREFILE, human); */
248/* setegid(gid); */
249
250/* /\* */
251/* * Grab a lock. */
252/* *\/ */
253/* if (flock(sd, lck)) */
254/* warn("warning: score file %s cannot be locked", */
255/* _PATH_SCOREFILE); */
256
257/* nscores = fread(scores, sizeof(scores[0]), MAXHISCORES, sf); */
258/* if (ferror(sf)) */
259/* err(1, "error reading %s", _PATH_SCOREFILE); */
260/* for (i = 0; i < nscores; i++) */
261/* if (scores[i].hs_level < MINLEVEL || */
262/* scores[i].hs_level > MAXLEVEL) */
263/* errx(1, "scorefile %s corrupt", _PATH_SCOREFILE); */
264
265/* if (fpp) */
266/* *fpp = sf; */
267/* else */
268/* (void)fclose(sf); */
269/* } */
270
271void
272savescore(int level)
273{
274 return;
275}
276/* struct highscore *sp; */
277/* int i; */
278/* int change; */
279/* FILE *sf; */
280/* const char *me; */
281
282/* getscores(&sf); */
283/* gotscores = 1; */
284/* (void)time(&now); */
285
286/* /\* */
287/* * Allow at most one score per person per level -- see if we */
288/* * can replace an existing score, or (easiest) do nothing. */
289/* * Otherwise add new score at end (there is always room). */
290/* *\/ */
291/* change = 0; */
292/* me = thisuser(); */
293/* for (i = 0, sp = &scores[0]; i < nscores; i++, sp++) { */
294/* if (sp->hs_level != level || str_cmp(sp->hs_name, me) != 0) */
295/* continue; */
296/* if (score > sp->hs_score) { */
297/* (void)printf("%s bettered %s %d score of %d!\n", */
298/* "\nYou", "your old level", level, */
299/* sp->hs_score * sp->hs_level); */
300/* sp->hs_score = score; /\* new score *\/ */
301/* sp->hs_time = now; /\* and time *\/ */
302/* change = 1; */
303/* } else if (score == sp->hs_score) { */
304/* (void)printf("%s tied %s %d high score.\n", */
305/* "\nYou", "your old level", level); */
306/* sp->hs_time = now; /\* renew it *\/ */
307/* change = 1; /\* gotta rewrite, sigh *\/ */
308/* } /\* else new score < old score: do nothing *\/ */
309/* break; */
310/* } */
311/* if (i >= nscores) { */
312/* strlcpy(sp->hs_name, me, sizeof sp->hs_name); */
313/* sp->hs_level = level; */
314/* sp->hs_score = score; */
315/* sp->hs_time = now; */
316/* nscores++; */
317/* change = 1; */
318/* } */
319
320/* if (change) { */
321/* /\* */
322/* * Sort & clean the scores, then rewrite. */
323/* *\/ */
324/* nscores = checkscores(scores, nscores); */
325/* rewind(sf); */
326/* if (fwrite(scores, sizeof(*sp), nscores, sf) != nscores || */
327/* fflush(sf) == EOF) */
328/* warnx("error writing %s: %s\n\t-- %s", */
329/* _PATH_SCOREFILE, strerror(errno), */
330/* "high scores may be damaged"); */
331/* } */
332/* (void)fclose(sf); /\* releases lock *\/ */
333/* } */
334
335/*
336 * Get login name, or if that fails, get something suitable.
337 * The result is always trimmed to fit in a score.
338 */
339/* static char * */
340/* thisuser(void) */
341/* { */
342/* const char *p; */
343/* struct passwd *pw; */
344/* static char u[sizeof(scores[0].hs_name)]; */
345
346/* if (u[0]) */
347/* return (u); */
348/* p = getlogin(); */
349/* if (p == NULL || *p == '\0') { */
350/* pw = getpwuid(getuid()); */
351/* if (pw != NULL) */
352/* p = pw->pw_name; */
353/* else */
354/* p = " ???"; */
355/* } */
356/* strlcpy(u, p, sizeof(u)); */
357/* return (u); */
358/* } */
359
360/*
361 * Score comparison function for qsort.
362 *
363 * If two scores are equal, the person who had the score first is
364 * listed first in the highscore file.
365 */
366/* static int */
367/* cmpscores(const void *x, const void *y) */
368/* { */
369/* const struct highscore *a, *b; */
370/* long l; */
371
372/* a = x; */
373/* b = y; */
374/* l = (long)b->hs_level * b->hs_score - (long)a->hs_level * a->hs_score; */
375/* if (l < 0) */
376/* return (-1); */
377/* if (l > 0) */
378/* return (1); */
379/* if (a->hs_time < b->hs_time) */
380/* return (-1); */
381/* if (a->hs_time > b->hs_time) */
382/* return (1); */
383/* return (0); */
384/* } */
385
386/*
387 * If we've added a score to the file, we need to check the file and ensure
388 * that this player has only a few entries. The number of entries is
389 * controlled by MAXSCORES, and is to ensure that the highscore file is not
390 * monopolised by just a few people. People who no longer have accounts are
391 * only allowed the highest score. Scores older than EXPIRATION seconds are
392 * removed, unless they are someone's personal best.
393 * Caveat: the highest score on each level is always kept.
394 */
395/* static int */
396/* checkscores(struct highscore *hs, int num) */
397/* { */
398/* struct highscore *sp; */
399/* int i, j, k, numnames; */
400/* int levelfound[NLEVELS]; */
401/* struct peruser { */
402/* char *name; */
403/* int times; */
404/* } count[NUMSPOTS]; */
405/* struct peruser *pu; */
406
407/* /\* */
408/* * Sort so that highest totals come first. */
409/* * */
410/* * levelfound[i] becomes set when the first high score for that */
411/* * level is encountered. By definition this is the highest score. */
412/* *\/ */
413/* qsort((void *)hs, nscores, sizeof(*hs), cmpscores); */
414/* for (i = MINLEVEL; i < NLEVELS; i++) */
415/* levelfound[i] = 0; */
416/* numnames = 0; */
417/* for (i = 0, sp = hs; i < num;) { */
418/* /\* */
419/* * This is O(n^2), but do you think we care? */
420/* *\/ */
421/* for (j = 0, pu = count; j < numnames; j++, pu++) */
422/* if (str_cmp(sp->hs_name, pu->name) == 0) */
423/* break; */
424/* if (j == numnames) { */
425/* /\* */
426/* * Add new user, set per-user count to 1. */
427/* *\/ */
428/* pu->name = sp->hs_name; */
429/* pu->times = 1; */
430/* numnames++; */
431/* } else { */
432/* /\* */
433/* * Two ways to keep this score: */
434/* * - Not too many (per user), still has acct, & */
435/* * score not dated; or */
436/* * - High score on this level. */
437/* *\/ */
438/* if ((pu->times < MAXSCORES && */
439/* getpwnam(sp->hs_name) != NULL && */
440/* sp->hs_time + EXPIRATION >= now) || */
441/* levelfound[sp->hs_level] == 0) */
442/* pu->times++; */
443/* else { */
444/* /\* */
445/* * Delete this score, do not count it, */
446/* * do not pass go, do not collect $200. */
447/* *\/ */
448/* num--; */
449/* for (k = i; k < num; k++) */
450/* hs[k] = hs[k + 1]; */
451/* continue; */
452/* } */
453/* } */
454/* levelfound[sp->hs_level] = 1; */
455/* i++, sp++; */
456/* } */
457/* return (num > MAXHISCORES ? MAXHISCORES : num); */
458/* } */
459
460/*
461 * Show current scores. This must be called after savescore, if
462 * savescore is called at all, for two reasons:
463 * - Showscores munches the time field.
464 * - Even if that were not the case, a new score must be recorded
465 * before it can be shown anyway.
466 */
467/*
468void
469showscores(int level)
470{
471 return;
472}
473*/
474/* struct highscore *sp; */
475/* int i, n, c; */
476/* const char *me; */
477/* int levelfound[NLEVELS]; */
478
479/* if (!gotscores) */
480/* getscores((FILE **)NULL); */
481/* (void)printf("\n\t\t Tetris High Scores\n"); */
482
483/* /\* */
484/* * If level == 0, the person has not played a game but just asked for */
485/* * the high scores; we do not need to check for printing in highlight */
486/* * mode. If SOstr is null, we can't do highlighting anyway. */
487/* *\/ */
488/* me = level && SOstr ? thisuser() : NULL; */
489
490/* /\* */
491/* * Set times to 0 except for high score on each level. */
492/* *\/ */
493/* for (i = MINLEVEL; i < NLEVELS; i++) */
494/* levelfound[i] = 0; */
495/* for (i = 0, sp = scores; i < nscores; i++, sp++) { */
496/* if (levelfound[sp->hs_level]) */
497/* sp->hs_time = 0; */
498/* else { */
499/* sp->hs_time = 1; */
500/* levelfound[sp->hs_level] = 1; */
501/* } */
502/* } */
503
504/* /\* */
505/* * Page each screenful of scores. */
506/* *\/ */
507/* for (i = 0, sp = scores; i < nscores; sp += n) { */
508/* n = 20; */
509/* if (i + n > nscores) */
510/* n = nscores - i; */
511/* printem(level, i + 1, sp, n, me); */
512/* if ((i += n) < nscores) { */
513/* (void)printf("\nHit RETURN to continue."); */
514/* (void)fflush(stdout); */
515/* while ((c = getchar()) != '\n') */
516/* if (c == EOF) */
517/* break; */
518/* (void)printf("\n"); */
519/* } */
520/* } */
521
522/* if (nscores == 0) */
523/* printf("\t\t\t - none to date.\n"); */
524/* } */
525
526/* static void */
527/* printem(int level, int offset, struct highscore *hs, int n, const char *me) */
528/* { */
529/* struct highscore *sp; */
530/* int row, highlight, i; */
531/* char buf[100]; */
532/* #define TITLE "Rank Score Name (points/level)" */
533/* #define TITL2 "==========================================================" */
534
535/* printf("%s\n%s\n", TITLE, TITL2); */
536
537/* highlight = 0; */
538
539/* for (row = 0; row < n; row++) { */
540/* sp = &hs[row]; */
541/* (void)snprintf(buf, sizeof(buf), */
542/* "%3d%c %6d %-31s (%6d on %d)\n", */
543/* row + offset, sp->hs_time ? '*' : ' ', */
544/* sp->hs_score * sp->hs_level, */
545/* sp->hs_name, sp->hs_score, sp->hs_level); */
546/* /\* Print leaders every three lines *\/ */
547/* if ((row + 1) % 3 == 0) { */
548/* for (i = 0; i < sizeof(buf); i++) */
549/* if (buf[i] == ' ') */
550/* buf[i] = '_'; */
551/* } */
552/* /\* */
553/* * Highlight if appropriate. This works because */
554/* * we only get one score per level. */
555/* *\/ */
556/* if (me != NULL && */
557/* sp->hs_level == level && */
558/* sp->hs_score == score && */
559/* str_cmp(sp->hs_name, me) == 0) { */
560/* putpad(SOstr); */
561/* highlight = 1; */
562/* } */
563/* (void)printf("%s", buf); */
564/* if (highlight) { */
565/* putpad(SEstr); */
566/* highlight = 0; */
567/* } */
568/* } */
569/* } */
570
571/** @}
572 */
573
Note: See TracBrowser for help on using the repository browser.