X-Git-Url: https://git.cameronkatri.com/bsdgames-darwin.git/blobdiff_plain/bb6c8ddc879dee011fca2ed7153f3b9393d3af8e..84d25b7fc1866e662d07f1e5d4b17f13c8547fc9:/tetris/scores.c diff --git a/tetris/scores.c b/tetris/scores.c index af3f348b..83b2c36a 100644 --- a/tetris/scores.c +++ b/tetris/scores.c @@ -1,4 +1,4 @@ -/* $NetBSD: scores.c,v 1.2 1995/04/22 07:42:38 cgd Exp $ */ +/* $NetBSD: scores.c,v 1.17 2009/06/01 04:03:26 dholland Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -15,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -45,20 +41,18 @@ * * Major whacks since then. */ +#include #include #include #include #include #include #include +#include #include +#include #include -/* - * XXX - need a - */ -int tputs __P((const char *, int, int (*)(int))); - #include "pathnames.h" #include "screen.h" #include "scores.h" @@ -80,32 +74,313 @@ static int nscores; static int gotscores; static struct highscore scores[NUMSPOTS]; -static int checkscores __P((struct highscore *, int)); -static int cmpscores __P((const void *, const void *)); -static void getscores __P((FILE **)); -static void printem __P((int, int, struct highscore *, int, const char *)); -static char *thisuser __P((void)); +static int checkscores(struct highscore *, int); +static int cmpscores(const void *, const void *); +static void getscores(int *); +static void printem(int, int, struct highscore *, int, const char *); +static char *thisuser(void); + +/* contents chosen to be a highly illegal username */ +static const char hsh_magic_val[HSH_MAGIC_SIZE] = "//:\0\0://"; + +#define HSH_ENDIAN_NATIVE 0x12345678 +#define HSH_ENDIAN_OPP 0x78563412 + +/* current file format version */ +#define HSH_VERSION 1 + +/* codes for scorefile_probe return */ +#define SCOREFILE_ERROR (-1) +#define SCOREFILE_CURRENT 0 /* 40-byte */ +#define SCOREFILE_CURRENT_OPP 1 /* 40-byte, opposite-endian */ +#define SCOREFILE_599 2 /* 36-byte */ +#define SCOREFILE_599_OPP 3 /* 36-byte, opposite-endian */ +#define SCOREFILE_50 4 /* 32-byte */ +#define SCOREFILE_50_OPP 5 /* 32-byte, opposite-endian */ + +/* + * Check (or guess) what kind of score file contents we have. + */ +static int +scorefile_probe(int sd) +{ + struct stat st; + int t1, t2, t3, tx; + ssize_t result; + uint32_t numbers[3], offset56, offset60, offset64; + + if (fstat(sd, &st) < 0) { + warn("Score file %s: fstat", _PATH_SCOREFILE); + return -1; + } + + t1 = st.st_size % sizeof(struct highscore_ondisk) == 0; + t2 = st.st_size % sizeof(struct highscore_ondisk_599) == 0; + t3 = st.st_size % sizeof(struct highscore_ondisk_50) == 0; + tx = t1 + t2 + t3; + if (tx == 1) { + /* Size matches exact number of one kind of records */ + if (t1) { + return SCOREFILE_CURRENT; + } else if (t2) { + return SCOREFILE_599; + } else { + return SCOREFILE_50; + } + } else if (tx == 0) { + /* Size matches nothing, pick most likely as default */ + goto wildguess; + } + + /* + * File size is multiple of more than one structure size. + * (For example, 288 bytes could be 9*hso50 or 8*hso599.) + * Read the file and see if we can figure out what's going + * on. This is the layout of the first two records: + * + * offset hso / current hso_599 hso_50 + * (40-byte) (36-byte) (32-byte) + * + * 0 name #0 name #0 name #0 + * 4 : : : + * 8 : : : + * 12 : : : + * 16 : : : + * 20 score #0 score #0 score #0 + * 24 level #0 level #0 level #0 + * 28 (pad) time #0 time #0 + * 32 time #0 name #1 + * 36 name #1 : + * 40 name #1 : : + * 44 : : : + * 48 : : : + * 52 : : score #1 + * 56 : score #1 level #1 + * 60 score #1 level #1 time #1 + * 64 level #1 time #1 name #2 + * 68 (pad) : : + * 72 time #1 name #2 : + * 76 : : : + * 80 --- end --- + * + * There are a number of things we could check here, but the + * most effective test is based on the following restrictions: + * + * - The level must be between 1 and 9 (inclusive) + * - All times must be after 1985 and are before 2038, + * so the high word must be 0 and the low word may not be + * a small value. + * - Integer values of 0 or 1-9 cannot be the beginning of + * a login name string. + * - Values of 1-9 are probably not a score. + * + * So we read the three words at offsets 56, 60, and 64, and + * poke at the values to try to figure things... + */ + + if (lseek(sd, 56, SEEK_SET) < 0) { + warn("Score file %s: lseek", _PATH_SCOREFILE); + return -1; + } + result = read(sd, &numbers, sizeof(numbers)); + if (result < 0) { + warn("Score file %s: read", _PATH_SCOREFILE); + return -1; + } + if ((size_t)result != sizeof(numbers)) { + /* + * The smallest file whose size divides by more than + * one of the sizes is substantially larger than 64, + * so this should *never* happen. + */ + warnx("Score file %s: Unexpected EOF", _PATH_SCOREFILE); + return -1; + } + + offset56 = numbers[0]; + offset60 = numbers[1]; + offset64 = numbers[2]; + + if (offset64 >= MINLEVEL && offset64 <= MAXLEVEL) { + /* 40-byte structure */ + return SCOREFILE_CURRENT; + } else if (offset60 >= MINLEVEL && offset60 <= MAXLEVEL) { + /* 36-byte structure */ + return SCOREFILE_599; + } else if (offset56 >= MINLEVEL && offset56 <= MAXLEVEL) { + /* 32-byte structure */ + return SCOREFILE_50; + } + + /* None was a valid level; try opposite endian */ + offset64 = bswap32(offset64); + offset60 = bswap32(offset60); + offset56 = bswap32(offset56); + + if (offset64 >= MINLEVEL && offset64 <= MAXLEVEL) { + /* 40-byte structure */ + return SCOREFILE_CURRENT_OPP; + } else if (offset60 >= MINLEVEL && offset60 <= MAXLEVEL) { + /* 36-byte structure */ + return SCOREFILE_599_OPP; + } else if (offset56 >= MINLEVEL && offset56 <= MAXLEVEL) { + /* 32-byte structure */ + return SCOREFILE_50_OPP; + } + + /* That didn't work either, dunno what's going on */ + wildguess: + warnx("Score file %s is likely corrupt", _PATH_SCOREFILE); + if (sizeof(void *) == 8 && sizeof(time_t) == 8) { + return SCOREFILE_CURRENT; + } else if (sizeof(time_t) == 8) { + return SCOREFILE_599; + } else { + return SCOREFILE_50; + } +} + +/* + * Copy a string safely, making sure it's null-terminated. + */ +static void +readname(char *to, size_t maxto, const char *from, size_t maxfrom) +{ + size_t amt; + + amt = maxto < maxfrom ? maxto : maxfrom; + memcpy(to, from, amt); + to[maxto-1] = '\0'; +} + +/* + * Copy integers, byte-swapping if desired. + */ +static int32_t +read32(int32_t val, int doflip) +{ + if (doflip) { + val = bswap32(val); + } + return val; +} + +static int64_t +read64(int64_t val, int doflip) +{ + if (doflip) { + val = bswap64(val); + } + return val; +} + +/* + * Read up to MAXHISCORES scorefile_ondisk entries. + */ +static int +readscores(int sd, int doflip) +{ + struct highscore_ondisk buf[MAXHISCORES]; + ssize_t result; + int i; + + result = read(sd, buf, sizeof(buf)); + if (result < 0) { + warn("Score file %s: read", _PATH_SCOREFILE); + return -1; + } + nscores = result / sizeof(buf[0]); + + for (i=0; i= sizeof(u)) l = sizeof(u) - 1; - bcopy(p, u, l); + memcpy(u, p, l); u[l] = '\0'; return (u); } @@ -252,11 +727,10 @@ thisuser() * listed first in the highscore file. */ static int -cmpscores(x, y) - const void *x, *y; +cmpscores(const void *x, const void *y) { - register const struct highscore *a, *b; - register long l; + const struct highscore *a, *b; + long l; a = x; b = y; @@ -282,18 +756,16 @@ cmpscores(x, y) * Caveat: the highest score on each level is always kept. */ static int -checkscores(hs, num) - register struct highscore *hs; - int num; +checkscores(struct highscore *hs, int num) { - register struct highscore *sp; - register int i, j, k, numnames; + struct highscore *sp; + int i, j, k, numnames; int levelfound[NLEVELS]; struct peruser { char *name; int times; } count[NUMSPOTS]; - register struct peruser *pu; + struct peruser *pu; /* * Sort so that highest totals come first. @@ -342,7 +814,8 @@ checkscores(hs, num) continue; } } - levelfound[sp->hs_level] = 1; + if (sp->hs_level < NLEVELS && sp->hs_level >= 0) + levelfound[sp->hs_level] = 1; i++, sp++; } return (num > MAXHISCORES ? MAXHISCORES : num); @@ -356,16 +829,15 @@ checkscores(hs, num) * before it can be shown anyway. */ void -showscores(level) - int level; +showscores(int level) { - register struct highscore *sp; - register int i, n, c; + struct highscore *sp; + int i, n, c; const char *me; int levelfound[NLEVELS]; if (!gotscores) - getscores((FILE **)NULL); + getscores(NULL); (void)printf("\n\t\t\t Tetris High Scores\n"); /* @@ -381,12 +853,14 @@ showscores(level) for (i = MINLEVEL; i < NLEVELS; i++) levelfound[i] = 0; for (i = 0, sp = scores; i < nscores; i++, sp++) { - if (levelfound[sp->hs_level]) - sp->hs_time = 0; - else { - sp->hs_time = 1; - levelfound[sp->hs_level] = 1; - } + if (sp->hs_level < NLEVELS && sp->hs_level >= 0) { + if (levelfound[sp->hs_level]) + sp->hs_time = 0; + else { + sp->hs_time = 1; + levelfound[sp->hs_level] = 1; + } + } } /* @@ -409,13 +883,9 @@ showscores(level) } static void -printem(level, offset, hs, n, me) - int level, offset; - register struct highscore *hs; - register int n; - const char *me; +printem(int level, int offset, struct highscore *hs, int n, const char *me) { - register struct highscore *sp; + struct highscore *sp; int nrows, row, col, item, i, highlight; char buf[100]; #define TITLE "Rank Score Name (points/level)" @@ -439,10 +909,9 @@ printem(level, offset, hs, n, me) (void)putchar('\n'); continue; } - (void)printf(item + offset < 10 ? " " : " "); sp = &hs[item]; - (void)sprintf(buf, - "%d%c %6d %-11s (%d on %d)", + (void)snprintf(buf, sizeof(buf), + "%3d%c %6d %-11s (%6d on %d)", item + offset, sp->hs_time ? '*' : ' ', sp->hs_score * sp->hs_level, sp->hs_name, sp->hs_score, sp->hs_level);