Blame


1 11a5e2cf 2024-01-16 benni /* $NetBSD: scores.c,v 1.26 2021/05/02 12:50:46 rillig Exp $ */
2 11a5e2cf 2024-01-16 benni
3 11a5e2cf 2024-01-16 benni /*-
4 11a5e2cf 2024-01-16 benni * Copyright (c) 1992, 1993
5 11a5e2cf 2024-01-16 benni * The Regents of the University of California. All rights reserved.
6 11a5e2cf 2024-01-16 benni *
7 11a5e2cf 2024-01-16 benni * This code is derived from software contributed to Berkeley by
8 11a5e2cf 2024-01-16 benni * Chris Torek and Darren F. Provine.
9 11a5e2cf 2024-01-16 benni *
10 11a5e2cf 2024-01-16 benni * Redistribution and use in source and binary forms, with or without
11 11a5e2cf 2024-01-16 benni * modification, are permitted provided that the following conditions
12 11a5e2cf 2024-01-16 benni * are met:
13 11a5e2cf 2024-01-16 benni * 1. Redistributions of source code must retain the above copyright
14 11a5e2cf 2024-01-16 benni * notice, this list of conditions and the following disclaimer.
15 11a5e2cf 2024-01-16 benni * 2. Redistributions in binary form must reproduce the above copyright
16 11a5e2cf 2024-01-16 benni * notice, this list of conditions and the following disclaimer in the
17 11a5e2cf 2024-01-16 benni * documentation and/or other materials provided with the distribution.
18 11a5e2cf 2024-01-16 benni * 3. Neither the name of the University nor the names of its contributors
19 11a5e2cf 2024-01-16 benni * may be used to endorse or promote products derived from this software
20 11a5e2cf 2024-01-16 benni * without specific prior written permission.
21 11a5e2cf 2024-01-16 benni *
22 11a5e2cf 2024-01-16 benni * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 11a5e2cf 2024-01-16 benni * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 11a5e2cf 2024-01-16 benni * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 11a5e2cf 2024-01-16 benni * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 11a5e2cf 2024-01-16 benni * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 11a5e2cf 2024-01-16 benni * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 11a5e2cf 2024-01-16 benni * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 11a5e2cf 2024-01-16 benni * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 11a5e2cf 2024-01-16 benni * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 11a5e2cf 2024-01-16 benni * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 11a5e2cf 2024-01-16 benni * SUCH DAMAGE.
33 11a5e2cf 2024-01-16 benni *
34 11a5e2cf 2024-01-16 benni * @(#)scores.c 8.1 (Berkeley) 5/31/93
35 11a5e2cf 2024-01-16 benni */
36 11a5e2cf 2024-01-16 benni
37 11a5e2cf 2024-01-16 benni /*
38 11a5e2cf 2024-01-16 benni * Score code for Tetris, by Darren Provine (kilroy@gboro.glassboro.edu)
39 11a5e2cf 2024-01-16 benni * modified 22 January 1992, to limit the number of entries any one
40 11a5e2cf 2024-01-16 benni * person has.
41 11a5e2cf 2024-01-16 benni *
42 11a5e2cf 2024-01-16 benni * Major whacks since then.
43 11a5e2cf 2024-01-16 benni */
44 11a5e2cf 2024-01-16 benni #include <err.h>
45 11a5e2cf 2024-01-16 benni #include <errno.h>
46 11a5e2cf 2024-01-16 benni #include <fcntl.h>
47 11a5e2cf 2024-01-16 benni #include <pwd.h>
48 11a5e2cf 2024-01-16 benni #include <stdio.h>
49 11a5e2cf 2024-01-16 benni #include <stdlib.h>
50 11a5e2cf 2024-01-16 benni #include <string.h>
51 11a5e2cf 2024-01-16 benni #include <sys/stat.h>
52 11a5e2cf 2024-01-16 benni #include <time.h>
53 11a5e2cf 2024-01-16 benni #include <term.h>
54 11a5e2cf 2024-01-16 benni #include <unistd.h>
55 84c906a7 2024-06-03 benni
56 84c906a7 2024-06-03 benni #ifdef __FreeBSD__
57 84c906a7 2024-06-03 benni # include <sys/endian.h>
58 84c906a7 2024-06-03 benni # define swap32 bswap32
59 84c906a7 2024-06-03 benni # define swap64 bswap64
60 7b083439 2024-06-06 benni #elif defined(__linux__)
61 7b083439 2024-06-06 benni # include <sys/file.h>
62 7b083439 2024-06-06 benni # define swap32 __builtin_bswap32
63 7b083439 2024-06-06 benni # define swap64 __builtin_bswap64
64 84c906a7 2024-06-03 benni #endif
65 11a5e2cf 2024-01-16 benni
66 11a5e2cf 2024-01-16 benni #include "pathnames.h"
67 11a5e2cf 2024-01-16 benni #include "screen.h"
68 11a5e2cf 2024-01-16 benni #include "scores.h"
69 11a5e2cf 2024-01-16 benni #include "tetris.h"
70 11a5e2cf 2024-01-16 benni
71 11a5e2cf 2024-01-16 benni /*
72 11a5e2cf 2024-01-16 benni * Allow updating the high scores unless we're built as part of /rescue.
73 11a5e2cf 2024-01-16 benni */
74 11a5e2cf 2024-01-16 benni #ifndef RESCUEDIR
75 11a5e2cf 2024-01-16 benni #define ALLOW_SCORE_UPDATES
76 11a5e2cf 2024-01-16 benni #endif
77 11a5e2cf 2024-01-16 benni
78 11a5e2cf 2024-01-16 benni /*
79 11a5e2cf 2024-01-16 benni * Within this code, we can hang onto one extra "high score", leaving
80 11a5e2cf 2024-01-16 benni * room for our current score (whether or not it is high).
81 11a5e2cf 2024-01-16 benni *
82 11a5e2cf 2024-01-16 benni * We also sometimes keep tabs on the "highest" score on each level.
83 11a5e2cf 2024-01-16 benni * As long as the scores are kept sorted, this is simply the first one at
84 11a5e2cf 2024-01-16 benni * that level.
85 11a5e2cf 2024-01-16 benni */
86 11a5e2cf 2024-01-16 benni #define NUMSPOTS (MAXHISCORES + 1)
87 11a5e2cf 2024-01-16 benni #define NLEVELS (MAXLEVEL + 1)
88 11a5e2cf 2024-01-16 benni
89 11a5e2cf 2024-01-16 benni static time_t now;
90 11a5e2cf 2024-01-16 benni static int nscores;
91 11a5e2cf 2024-01-16 benni static int gotscores;
92 11a5e2cf 2024-01-16 benni static struct highscore scores[NUMSPOTS];
93 11a5e2cf 2024-01-16 benni
94 11a5e2cf 2024-01-16 benni static int checkscores(struct highscore *, int);
95 11a5e2cf 2024-01-16 benni static int cmpscores(const void *, const void *);
96 11a5e2cf 2024-01-16 benni static void getscores(int *);
97 11a5e2cf 2024-01-16 benni static void printem(int, int, struct highscore *, int, const char *);
98 11a5e2cf 2024-01-16 benni static char *thisuser(void);
99 11a5e2cf 2024-01-16 benni
100 11a5e2cf 2024-01-16 benni /* contents chosen to be a highly illegal username */
101 11a5e2cf 2024-01-16 benni static const char hsh_magic_val[HSH_MAGIC_SIZE] = "//:\0\0://";
102 11a5e2cf 2024-01-16 benni
103 11a5e2cf 2024-01-16 benni #define HSH_ENDIAN_NATIVE 0x12345678
104 11a5e2cf 2024-01-16 benni #define HSH_ENDIAN_OPP 0x78563412
105 11a5e2cf 2024-01-16 benni
106 11a5e2cf 2024-01-16 benni /* current file format version */
107 11a5e2cf 2024-01-16 benni #define HSH_VERSION 1
108 11a5e2cf 2024-01-16 benni
109 11a5e2cf 2024-01-16 benni /* codes for scorefile_probe return */
110 11a5e2cf 2024-01-16 benni #define SCOREFILE_ERROR (-1)
111 11a5e2cf 2024-01-16 benni #define SCOREFILE_CURRENT 0 /* 40-byte */
112 11a5e2cf 2024-01-16 benni #define SCOREFILE_CURRENT_OPP 1 /* 40-byte, opposite-endian */
113 11a5e2cf 2024-01-16 benni #define SCOREFILE_599 2 /* 36-byte */
114 11a5e2cf 2024-01-16 benni #define SCOREFILE_599_OPP 3 /* 36-byte, opposite-endian */
115 11a5e2cf 2024-01-16 benni #define SCOREFILE_50 4 /* 32-byte */
116 11a5e2cf 2024-01-16 benni #define SCOREFILE_50_OPP 5 /* 32-byte, opposite-endian */
117 11a5e2cf 2024-01-16 benni
118 11a5e2cf 2024-01-16 benni /*
119 11a5e2cf 2024-01-16 benni * Check (or guess) what kind of score file contents we have.
120 11a5e2cf 2024-01-16 benni */
121 11a5e2cf 2024-01-16 benni static int
122 11a5e2cf 2024-01-16 benni scorefile_probe(int sd)
123 11a5e2cf 2024-01-16 benni {
124 11a5e2cf 2024-01-16 benni struct stat st;
125 11a5e2cf 2024-01-16 benni int t1, t2, t3, tx;
126 11a5e2cf 2024-01-16 benni ssize_t result;
127 11a5e2cf 2024-01-16 benni uint32_t numbers[3], offset56, offset60, offset64;
128 11a5e2cf 2024-01-16 benni
129 11a5e2cf 2024-01-16 benni if (fstat(sd, &st) < 0) {
130 11a5e2cf 2024-01-16 benni warn("Score file %s: fstat", _PATH_SCOREFILE);
131 11a5e2cf 2024-01-16 benni return -1;
132 11a5e2cf 2024-01-16 benni }
133 11a5e2cf 2024-01-16 benni
134 11a5e2cf 2024-01-16 benni t1 = st.st_size % sizeof(struct highscore_ondisk) == 0;
135 11a5e2cf 2024-01-16 benni t2 = st.st_size % sizeof(struct highscore_ondisk_599) == 0;
136 11a5e2cf 2024-01-16 benni t3 = st.st_size % sizeof(struct highscore_ondisk_50) == 0;
137 11a5e2cf 2024-01-16 benni tx = t1 + t2 + t3;
138 11a5e2cf 2024-01-16 benni if (tx == 1) {
139 11a5e2cf 2024-01-16 benni /* Size matches exact number of one kind of records */
140 11a5e2cf 2024-01-16 benni if (t1) {
141 11a5e2cf 2024-01-16 benni return SCOREFILE_CURRENT;
142 11a5e2cf 2024-01-16 benni } else if (t2) {
143 11a5e2cf 2024-01-16 benni return SCOREFILE_599;
144 11a5e2cf 2024-01-16 benni } else {
145 11a5e2cf 2024-01-16 benni return SCOREFILE_50;
146 11a5e2cf 2024-01-16 benni }
147 11a5e2cf 2024-01-16 benni } else if (tx == 0) {
148 11a5e2cf 2024-01-16 benni /* Size matches nothing, pick most likely as default */
149 11a5e2cf 2024-01-16 benni goto wildguess;
150 11a5e2cf 2024-01-16 benni }
151 11a5e2cf 2024-01-16 benni
152 11a5e2cf 2024-01-16 benni /*
153 11a5e2cf 2024-01-16 benni * File size is multiple of more than one structure size.
154 11a5e2cf 2024-01-16 benni * (For example, 288 bytes could be 9*hso50 or 8*hso599.)
155 11a5e2cf 2024-01-16 benni * Read the file and see if we can figure out what's going
156 11a5e2cf 2024-01-16 benni * on. This is the layout of the first two records:
157 11a5e2cf 2024-01-16 benni *
158 11a5e2cf 2024-01-16 benni * offset hso / current hso_599 hso_50
159 11a5e2cf 2024-01-16 benni * (40-byte) (36-byte) (32-byte)
160 11a5e2cf 2024-01-16 benni *
161 11a5e2cf 2024-01-16 benni * 0 name #0 name #0 name #0
162 11a5e2cf 2024-01-16 benni * 4 : : :
163 11a5e2cf 2024-01-16 benni * 8 : : :
164 11a5e2cf 2024-01-16 benni * 12 : : :
165 11a5e2cf 2024-01-16 benni * 16 : : :
166 11a5e2cf 2024-01-16 benni * 20 score #0 score #0 score #0
167 11a5e2cf 2024-01-16 benni * 24 level #0 level #0 level #0
168 11a5e2cf 2024-01-16 benni * 28 (pad) time #0 time #0
169 11a5e2cf 2024-01-16 benni * 32 time #0 name #1
170 11a5e2cf 2024-01-16 benni * 36 name #1 :
171 11a5e2cf 2024-01-16 benni * 40 name #1 : :
172 11a5e2cf 2024-01-16 benni * 44 : : :
173 11a5e2cf 2024-01-16 benni * 48 : : :
174 11a5e2cf 2024-01-16 benni * 52 : : score #1
175 11a5e2cf 2024-01-16 benni * 56 : score #1 level #1
176 11a5e2cf 2024-01-16 benni * 60 score #1 level #1 time #1
177 11a5e2cf 2024-01-16 benni * 64 level #1 time #1 name #2
178 11a5e2cf 2024-01-16 benni * 68 (pad) : :
179 11a5e2cf 2024-01-16 benni * 72 time #1 name #2 :
180 11a5e2cf 2024-01-16 benni * 76 : : :
181 11a5e2cf 2024-01-16 benni * 80 --- end ---
182 11a5e2cf 2024-01-16 benni *
183 11a5e2cf 2024-01-16 benni * There are a number of things we could check here, but the
184 11a5e2cf 2024-01-16 benni * most effective test is based on the following restrictions:
185 11a5e2cf 2024-01-16 benni *
186 11a5e2cf 2024-01-16 benni * - The level must be between 1 and 9 (inclusive)
187 11a5e2cf 2024-01-16 benni * - All times must be after 1985 and are before 2038,
188 11a5e2cf 2024-01-16 benni * so the high word must be 0 and the low word may not be
189 11a5e2cf 2024-01-16 benni * a small value.
190 11a5e2cf 2024-01-16 benni * - Integer values of 0 or 1-9 cannot be the beginning of
191 11a5e2cf 2024-01-16 benni * a login name string.
192 11a5e2cf 2024-01-16 benni * - Values of 1-9 are probably not a score.
193 11a5e2cf 2024-01-16 benni *
194 11a5e2cf 2024-01-16 benni * So we read the three words at offsets 56, 60, and 64, and
195 11a5e2cf 2024-01-16 benni * poke at the values to try to figure things...
196 11a5e2cf 2024-01-16 benni */
197 11a5e2cf 2024-01-16 benni
198 11a5e2cf 2024-01-16 benni if (lseek(sd, 56, SEEK_SET) < 0) {
199 11a5e2cf 2024-01-16 benni warn("Score file %s: lseek", _PATH_SCOREFILE);
200 11a5e2cf 2024-01-16 benni return -1;
201 11a5e2cf 2024-01-16 benni }
202 11a5e2cf 2024-01-16 benni result = read(sd, &numbers, sizeof(numbers));
203 11a5e2cf 2024-01-16 benni if (result < 0) {
204 11a5e2cf 2024-01-16 benni warn("Score file %s: read", _PATH_SCOREFILE);
205 11a5e2cf 2024-01-16 benni return -1;
206 11a5e2cf 2024-01-16 benni }
207 11a5e2cf 2024-01-16 benni if ((size_t)result != sizeof(numbers)) {
208 11a5e2cf 2024-01-16 benni /*
209 11a5e2cf 2024-01-16 benni * The smallest file whose size divides by more than
210 11a5e2cf 2024-01-16 benni * one of the sizes is substantially larger than 64,
211 11a5e2cf 2024-01-16 benni * so this should *never* happen.
212 11a5e2cf 2024-01-16 benni */
213 11a5e2cf 2024-01-16 benni warnx("Score file %s: Unexpected EOF", _PATH_SCOREFILE);
214 11a5e2cf 2024-01-16 benni return -1;
215 11a5e2cf 2024-01-16 benni }
216 11a5e2cf 2024-01-16 benni
217 11a5e2cf 2024-01-16 benni offset56 = numbers[0];
218 11a5e2cf 2024-01-16 benni offset60 = numbers[1];
219 11a5e2cf 2024-01-16 benni offset64 = numbers[2];
220 11a5e2cf 2024-01-16 benni
221 11a5e2cf 2024-01-16 benni if (offset64 >= MINLEVEL && offset64 <= MAXLEVEL) {
222 11a5e2cf 2024-01-16 benni /* 40-byte structure */
223 11a5e2cf 2024-01-16 benni return SCOREFILE_CURRENT;
224 11a5e2cf 2024-01-16 benni } else if (offset60 >= MINLEVEL && offset60 <= MAXLEVEL) {
225 11a5e2cf 2024-01-16 benni /* 36-byte structure */
226 11a5e2cf 2024-01-16 benni return SCOREFILE_599;
227 11a5e2cf 2024-01-16 benni } else if (offset56 >= MINLEVEL && offset56 <= MAXLEVEL) {
228 11a5e2cf 2024-01-16 benni /* 32-byte structure */
229 11a5e2cf 2024-01-16 benni return SCOREFILE_50;
230 11a5e2cf 2024-01-16 benni }
231 11a5e2cf 2024-01-16 benni
232 11a5e2cf 2024-01-16 benni /* None was a valid level; try opposite endian */
233 60af9557 2024-01-16 benni offset64 = swap32(offset64);
234 60af9557 2024-01-16 benni offset60 = swap32(offset60);
235 60af9557 2024-01-16 benni offset56 = swap32(offset56);
236 11a5e2cf 2024-01-16 benni
237 11a5e2cf 2024-01-16 benni if (offset64 >= MINLEVEL && offset64 <= MAXLEVEL) {
238 11a5e2cf 2024-01-16 benni /* 40-byte structure */
239 11a5e2cf 2024-01-16 benni return SCOREFILE_CURRENT_OPP;
240 11a5e2cf 2024-01-16 benni } else if (offset60 >= MINLEVEL && offset60 <= MAXLEVEL) {
241 11a5e2cf 2024-01-16 benni /* 36-byte structure */
242 11a5e2cf 2024-01-16 benni return SCOREFILE_599_OPP;
243 11a5e2cf 2024-01-16 benni } else if (offset56 >= MINLEVEL && offset56 <= MAXLEVEL) {
244 11a5e2cf 2024-01-16 benni /* 32-byte structure */
245 11a5e2cf 2024-01-16 benni return SCOREFILE_50_OPP;
246 11a5e2cf 2024-01-16 benni }
247 11a5e2cf 2024-01-16 benni
248 11a5e2cf 2024-01-16 benni /* That didn't work either, dunno what's going on */
249 11a5e2cf 2024-01-16 benni wildguess:
250 11a5e2cf 2024-01-16 benni warnx("Score file %s is likely corrupt", _PATH_SCOREFILE);
251 11a5e2cf 2024-01-16 benni if (sizeof(void *) == 8 && sizeof(time_t) == 8) {
252 11a5e2cf 2024-01-16 benni return SCOREFILE_CURRENT;
253 11a5e2cf 2024-01-16 benni } else if (sizeof(time_t) == 8) {
254 11a5e2cf 2024-01-16 benni return SCOREFILE_599;
255 11a5e2cf 2024-01-16 benni } else {
256 11a5e2cf 2024-01-16 benni return SCOREFILE_50;
257 11a5e2cf 2024-01-16 benni }
258 11a5e2cf 2024-01-16 benni }
259 11a5e2cf 2024-01-16 benni
260 11a5e2cf 2024-01-16 benni /*
261 11a5e2cf 2024-01-16 benni * Copy a string safely, making sure it's null-terminated.
262 11a5e2cf 2024-01-16 benni */
263 11a5e2cf 2024-01-16 benni static void
264 11a5e2cf 2024-01-16 benni readname(char *to, size_t maxto, const char *from, size_t maxfrom)
265 11a5e2cf 2024-01-16 benni {
266 11a5e2cf 2024-01-16 benni size_t amt;
267 11a5e2cf 2024-01-16 benni
268 11a5e2cf 2024-01-16 benni amt = maxto < maxfrom ? maxto : maxfrom;
269 11a5e2cf 2024-01-16 benni memcpy(to, from, amt);
270 11a5e2cf 2024-01-16 benni to[maxto-1] = '\0';
271 11a5e2cf 2024-01-16 benni }
272 11a5e2cf 2024-01-16 benni
273 11a5e2cf 2024-01-16 benni /*
274 11a5e2cf 2024-01-16 benni * Copy integers, byte-swapping if desired.
275 11a5e2cf 2024-01-16 benni */
276 11a5e2cf 2024-01-16 benni static int32_t
277 11a5e2cf 2024-01-16 benni read32(int32_t val, int doflip)
278 11a5e2cf 2024-01-16 benni {
279 11a5e2cf 2024-01-16 benni if (doflip) {
280 60af9557 2024-01-16 benni val = swap32(val);
281 11a5e2cf 2024-01-16 benni }
282 11a5e2cf 2024-01-16 benni return val;
283 11a5e2cf 2024-01-16 benni }
284 11a5e2cf 2024-01-16 benni
285 11a5e2cf 2024-01-16 benni static int64_t
286 11a5e2cf 2024-01-16 benni read64(int64_t val, int doflip)
287 11a5e2cf 2024-01-16 benni {
288 11a5e2cf 2024-01-16 benni if (doflip) {
289 60af9557 2024-01-16 benni val = swap64(val);
290 11a5e2cf 2024-01-16 benni }
291 11a5e2cf 2024-01-16 benni return val;
292 11a5e2cf 2024-01-16 benni }
293 11a5e2cf 2024-01-16 benni
294 11a5e2cf 2024-01-16 benni /*
295 11a5e2cf 2024-01-16 benni * Read up to MAXHISCORES scorefile_ondisk entries.
296 11a5e2cf 2024-01-16 benni */
297 11a5e2cf 2024-01-16 benni static int
298 11a5e2cf 2024-01-16 benni readscores(int sd, int doflip)
299 11a5e2cf 2024-01-16 benni {
300 11a5e2cf 2024-01-16 benni struct highscore_ondisk buf[MAXHISCORES];
301 11a5e2cf 2024-01-16 benni ssize_t result;
302 11a5e2cf 2024-01-16 benni int i;
303 11a5e2cf 2024-01-16 benni
304 11a5e2cf 2024-01-16 benni result = read(sd, buf, sizeof(buf));
305 11a5e2cf 2024-01-16 benni if (result < 0) {
306 11a5e2cf 2024-01-16 benni warn("Score file %s: read", _PATH_SCOREFILE);
307 11a5e2cf 2024-01-16 benni return -1;
308 11a5e2cf 2024-01-16 benni }
309 11a5e2cf 2024-01-16 benni nscores = result / sizeof(buf[0]);
310 11a5e2cf 2024-01-16 benni
311 11a5e2cf 2024-01-16 benni for (i=0; i<nscores; i++) {
312 11a5e2cf 2024-01-16 benni readname(scores[i].hs_name, sizeof(scores[i].hs_name),
313 11a5e2cf 2024-01-16 benni buf[i].hso_name, sizeof(buf[i].hso_name));
314 11a5e2cf 2024-01-16 benni scores[i].hs_score = read32(buf[i].hso_score, doflip);
315 11a5e2cf 2024-01-16 benni scores[i].hs_level = read32(buf[i].hso_level, doflip);
316 11a5e2cf 2024-01-16 benni scores[i].hs_time = read64(buf[i].hso_time, doflip);
317 11a5e2cf 2024-01-16 benni }
318 11a5e2cf 2024-01-16 benni return 0;
319 11a5e2cf 2024-01-16 benni }
320 11a5e2cf 2024-01-16 benni
321 11a5e2cf 2024-01-16 benni /*
322 11a5e2cf 2024-01-16 benni * Read up to MAXHISCORES scorefile_ondisk_599 entries.
323 11a5e2cf 2024-01-16 benni */
324 11a5e2cf 2024-01-16 benni static int
325 11a5e2cf 2024-01-16 benni readscores599(int sd, int doflip)
326 11a5e2cf 2024-01-16 benni {
327 11a5e2cf 2024-01-16 benni struct highscore_ondisk_599 buf[MAXHISCORES];
328 11a5e2cf 2024-01-16 benni ssize_t result;
329 11a5e2cf 2024-01-16 benni int i;
330 11a5e2cf 2024-01-16 benni
331 11a5e2cf 2024-01-16 benni result = read(sd, buf, sizeof(buf));
332 11a5e2cf 2024-01-16 benni if (result < 0) {
333 11a5e2cf 2024-01-16 benni warn("Score file %s: read", _PATH_SCOREFILE);
334 11a5e2cf 2024-01-16 benni return -1;
335 11a5e2cf 2024-01-16 benni }
336 11a5e2cf 2024-01-16 benni nscores = result / sizeof(buf[0]);
337 11a5e2cf 2024-01-16 benni
338 11a5e2cf 2024-01-16 benni for (i=0; i<nscores; i++) {
339 11a5e2cf 2024-01-16 benni readname(scores[i].hs_name, sizeof(scores[i].hs_name),
340 11a5e2cf 2024-01-16 benni buf[i].hso599_name, sizeof(buf[i].hso599_name));
341 11a5e2cf 2024-01-16 benni scores[i].hs_score = read32(buf[i].hso599_score, doflip);
342 11a5e2cf 2024-01-16 benni scores[i].hs_level = read32(buf[i].hso599_level, doflip);
343 11a5e2cf 2024-01-16 benni /*
344 11a5e2cf 2024-01-16 benni * Don't bother pasting the time together into a
345 11a5e2cf 2024-01-16 benni * 64-bit value; just take whichever half is nonzero.
346 11a5e2cf 2024-01-16 benni */
347 11a5e2cf 2024-01-16 benni scores[i].hs_time =
348 11a5e2cf 2024-01-16 benni read32(buf[i].hso599_time[buf[i].hso599_time[0] == 0],
349 11a5e2cf 2024-01-16 benni doflip);
350 11a5e2cf 2024-01-16 benni }
351 11a5e2cf 2024-01-16 benni return 0;
352 11a5e2cf 2024-01-16 benni }
353 11a5e2cf 2024-01-16 benni
354 11a5e2cf 2024-01-16 benni /*
355 11a5e2cf 2024-01-16 benni * Read up to MAXHISCORES scorefile_ondisk_50 entries.
356 11a5e2cf 2024-01-16 benni */
357 11a5e2cf 2024-01-16 benni static int
358 11a5e2cf 2024-01-16 benni readscores50(int sd, int doflip)
359 11a5e2cf 2024-01-16 benni {
360 11a5e2cf 2024-01-16 benni struct highscore_ondisk_50 buf[MAXHISCORES];
361 11a5e2cf 2024-01-16 benni ssize_t result;
362 11a5e2cf 2024-01-16 benni int i;
363 11a5e2cf 2024-01-16 benni
364 11a5e2cf 2024-01-16 benni result = read(sd, buf, sizeof(buf));
365 11a5e2cf 2024-01-16 benni if (result < 0) {
366 11a5e2cf 2024-01-16 benni warn("Score file %s: read", _PATH_SCOREFILE);
367 11a5e2cf 2024-01-16 benni return -1;
368 11a5e2cf 2024-01-16 benni }
369 11a5e2cf 2024-01-16 benni nscores = result / sizeof(buf[0]);
370 11a5e2cf 2024-01-16 benni
371 11a5e2cf 2024-01-16 benni for (i=0; i<nscores; i++) {
372 11a5e2cf 2024-01-16 benni readname(scores[i].hs_name, sizeof(scores[i].hs_name),
373 11a5e2cf 2024-01-16 benni buf[i].hso50_name, sizeof(buf[i].hso50_name));
374 11a5e2cf 2024-01-16 benni scores[i].hs_score = read32(buf[i].hso50_score, doflip);
375 11a5e2cf 2024-01-16 benni scores[i].hs_level = read32(buf[i].hso50_level, doflip);
376 11a5e2cf 2024-01-16 benni scores[i].hs_time = read32(buf[i].hso50_time, doflip);
377 11a5e2cf 2024-01-16 benni }
378 11a5e2cf 2024-01-16 benni return 0;
379 11a5e2cf 2024-01-16 benni }
380 11a5e2cf 2024-01-16 benni
381 11a5e2cf 2024-01-16 benni /*
382 11a5e2cf 2024-01-16 benni * Read the score file. Can be called from savescore (before showscores)
383 11a5e2cf 2024-01-16 benni * or showscores (if savescore will not be called). If the given pointer
384 11a5e2cf 2024-01-16 benni * is not NULL, sets *fdp to an open file handle that corresponds to a
385 11a5e2cf 2024-01-16 benni * read/write score file that is locked with LOCK_EX. Otherwise, the
386 11a5e2cf 2024-01-16 benni * file is locked with LOCK_SH for the read and closed before return.
387 11a5e2cf 2024-01-16 benni */
388 11a5e2cf 2024-01-16 benni static void
389 11a5e2cf 2024-01-16 benni getscores(int *fdp)
390 11a5e2cf 2024-01-16 benni {
391 11a5e2cf 2024-01-16 benni struct highscore_header header;
392 11a5e2cf 2024-01-16 benni int sd, mint, lck;
393 11a5e2cf 2024-01-16 benni mode_t mask;
394 11a5e2cf 2024-01-16 benni const char *human;
395 11a5e2cf 2024-01-16 benni int doflip;
396 11a5e2cf 2024-01-16 benni int serrno;
397 11a5e2cf 2024-01-16 benni ssize_t result;
398 11a5e2cf 2024-01-16 benni
399 11a5e2cf 2024-01-16 benni #ifdef ALLOW_SCORE_UPDATES
400 11a5e2cf 2024-01-16 benni if (fdp != NULL) {
401 11a5e2cf 2024-01-16 benni mint = O_RDWR | O_CREAT;
402 11a5e2cf 2024-01-16 benni human = "read/write";
403 11a5e2cf 2024-01-16 benni lck = LOCK_EX;
404 11a5e2cf 2024-01-16 benni } else
405 11a5e2cf 2024-01-16 benni #endif
406 11a5e2cf 2024-01-16 benni {
407 11a5e2cf 2024-01-16 benni mint = O_RDONLY;
408 11a5e2cf 2024-01-16 benni human = "reading";
409 11a5e2cf 2024-01-16 benni lck = LOCK_SH;
410 11a5e2cf 2024-01-16 benni }
411 11a5e2cf 2024-01-16 benni setegid(egid);
412 11a5e2cf 2024-01-16 benni mask = umask(S_IWOTH);
413 11a5e2cf 2024-01-16 benni sd = open(_PATH_SCOREFILE, mint, 0666);
414 11a5e2cf 2024-01-16 benni serrno = errno;
415 11a5e2cf 2024-01-16 benni (void)umask(mask);
416 11a5e2cf 2024-01-16 benni setegid(gid);
417 11a5e2cf 2024-01-16 benni if (sd < 0) {
418 11a5e2cf 2024-01-16 benni /*
419 11a5e2cf 2024-01-16 benni * If the file simply isn't there because nobody's
420 11a5e2cf 2024-01-16 benni * played yet, and we aren't going to be trying to
421 11a5e2cf 2024-01-16 benni * update it, don't warn. Even if we are going to be
422 11a5e2cf 2024-01-16 benni * trying to write it, don't fail -- we can still show
423 11a5e2cf 2024-01-16 benni * the player the score they got.
424 11a5e2cf 2024-01-16 benni */
425 11a5e2cf 2024-01-16 benni errno = serrno;
426 11a5e2cf 2024-01-16 benni if (fdp != NULL || errno != ENOENT) {
427 11a5e2cf 2024-01-16 benni warn("Cannot open %s for %s", _PATH_SCOREFILE, human);
428 11a5e2cf 2024-01-16 benni }
429 11a5e2cf 2024-01-16 benni goto fail;
430 11a5e2cf 2024-01-16 benni }
431 11a5e2cf 2024-01-16 benni
432 11a5e2cf 2024-01-16 benni /*
433 11a5e2cf 2024-01-16 benni * Grab a lock.
434 11a5e2cf 2024-01-16 benni * XXX: failure here should probably be more fatal than this.
435 11a5e2cf 2024-01-16 benni */
436 11a5e2cf 2024-01-16 benni if (flock(sd, lck))
437 11a5e2cf 2024-01-16 benni warn("warning: score file %s cannot be locked",
438 11a5e2cf 2024-01-16 benni _PATH_SCOREFILE);
439 11a5e2cf 2024-01-16 benni
440 11a5e2cf 2024-01-16 benni /*
441 11a5e2cf 2024-01-16 benni * The current format (since -current of 20090525) is
442 11a5e2cf 2024-01-16 benni *
443 11a5e2cf 2024-01-16 benni * struct highscore_header
444 11a5e2cf 2024-01-16 benni * up to MAXHIGHSCORES x struct highscore_ondisk
445 11a5e2cf 2024-01-16 benni *
446 11a5e2cf 2024-01-16 benni * Before this, there is no header, and the contents
447 11a5e2cf 2024-01-16 benni * might be any of three formats:
448 11a5e2cf 2024-01-16 benni *
449 11a5e2cf 2024-01-16 benni * highscore_ondisk (64-bit machines with 64-bit time_t)
450 11a5e2cf 2024-01-16 benni * highscore_ondisk_599 (32-bit machines with 64-bit time_t)
451 11a5e2cf 2024-01-16 benni * highscore_ondisk_50 (32-bit machines with 32-bit time_t)
452 11a5e2cf 2024-01-16 benni *
453 11a5e2cf 2024-01-16 benni * The first two appear in 5.99 between the time_t change and
454 11a5e2cf 2024-01-16 benni * 20090525, depending on whether the compiler inserts
455 11a5e2cf 2024-01-16 benni * structure padding before an unaligned 64-bit time_t. The
456 11a5e2cf 2024-01-16 benni * last appears in 5.0 and earlier.
457 11a5e2cf 2024-01-16 benni *
458 11a5e2cf 2024-01-16 benni * Any or all of these might also appear on other OSes where
459 11a5e2cf 2024-01-16 benni * this code has been ported.
460 11a5e2cf 2024-01-16 benni *
461 11a5e2cf 2024-01-16 benni * Since the old file has no header, we will have to guess
462 11a5e2cf 2024-01-16 benni * which of these formats it has.
463 11a5e2cf 2024-01-16 benni */
464 11a5e2cf 2024-01-16 benni
465 11a5e2cf 2024-01-16 benni /*
466 11a5e2cf 2024-01-16 benni * First, look for a header.
467 11a5e2cf 2024-01-16 benni */
468 11a5e2cf 2024-01-16 benni result = read(sd, &header, sizeof(header));
469 11a5e2cf 2024-01-16 benni if (result < 0) {
470 11a5e2cf 2024-01-16 benni warn("Score file %s: read", _PATH_SCOREFILE);
471 11a5e2cf 2024-01-16 benni goto sdfail;
472 11a5e2cf 2024-01-16 benni }
473 11a5e2cf 2024-01-16 benni if (result != 0 && (size_t)result != sizeof(header)) {
474 11a5e2cf 2024-01-16 benni warnx("Score file %s: read: unexpected EOF", _PATH_SCOREFILE);
475 11a5e2cf 2024-01-16 benni /*
476 11a5e2cf 2024-01-16 benni * File is hopelessly corrupt, might as well truncate it
477 11a5e2cf 2024-01-16 benni * and start over with empty scores.
478 11a5e2cf 2024-01-16 benni */
479 11a5e2cf 2024-01-16 benni if (lseek(sd, 0, SEEK_SET) < 0) {
480 11a5e2cf 2024-01-16 benni /* ? */
481 11a5e2cf 2024-01-16 benni warn("Score file %s: lseek", _PATH_SCOREFILE);
482 11a5e2cf 2024-01-16 benni goto sdfail;
483 11a5e2cf 2024-01-16 benni }
484 11a5e2cf 2024-01-16 benni if (ftruncate(sd, 0) == 0) {
485 11a5e2cf 2024-01-16 benni result = 0;
486 11a5e2cf 2024-01-16 benni } else {
487 11a5e2cf 2024-01-16 benni goto sdfail;
488 11a5e2cf 2024-01-16 benni }
489 11a5e2cf 2024-01-16 benni }
490 11a5e2cf 2024-01-16 benni
491 11a5e2cf 2024-01-16 benni if (result == 0) {
492 11a5e2cf 2024-01-16 benni /* Empty file; that just means there are no scores. */
493 11a5e2cf 2024-01-16 benni nscores = 0;
494 11a5e2cf 2024-01-16 benni } else {
495 11a5e2cf 2024-01-16 benni /*
496 11a5e2cf 2024-01-16 benni * Is what we read a header, or the first 16 bytes of
497 11a5e2cf 2024-01-16 benni * a score entry? hsh_magic_val is chosen to be
498 11a5e2cf 2024-01-16 benni * something that is extremely unlikely to appear in
499 11a5e2cf 2024-01-16 benni * hs_name[].
500 11a5e2cf 2024-01-16 benni */
501 11a5e2cf 2024-01-16 benni if (!memcmp(header.hsh_magic, hsh_magic_val, HSH_MAGIC_SIZE)) {
502 11a5e2cf 2024-01-16 benni /* Yes, we have a header. */
503 11a5e2cf 2024-01-16 benni
504 11a5e2cf 2024-01-16 benni if (header.hsh_endiantag == HSH_ENDIAN_NATIVE) {
505 11a5e2cf 2024-01-16 benni /* native endian */
506 11a5e2cf 2024-01-16 benni doflip = 0;
507 11a5e2cf 2024-01-16 benni } else if (header.hsh_endiantag == HSH_ENDIAN_OPP) {
508 11a5e2cf 2024-01-16 benni doflip = 1;
509 11a5e2cf 2024-01-16 benni } else {
510 11a5e2cf 2024-01-16 benni warnx("Score file %s: Unknown endian tag %u",
511 11a5e2cf 2024-01-16 benni _PATH_SCOREFILE, header.hsh_endiantag);
512 11a5e2cf 2024-01-16 benni goto sdfail;
513 11a5e2cf 2024-01-16 benni }
514 11a5e2cf 2024-01-16 benni
515 11a5e2cf 2024-01-16 benni if (header.hsh_version != HSH_VERSION) {
516 11a5e2cf 2024-01-16 benni warnx("Score file %s: Unknown version code %u",
517 11a5e2cf 2024-01-16 benni _PATH_SCOREFILE, header.hsh_version);
518 11a5e2cf 2024-01-16 benni goto sdfail;
519 11a5e2cf 2024-01-16 benni }
520 11a5e2cf 2024-01-16 benni
521 11a5e2cf 2024-01-16 benni if (readscores(sd, doflip) < 0) {
522 11a5e2cf 2024-01-16 benni goto sdfail;
523 11a5e2cf 2024-01-16 benni }
524 11a5e2cf 2024-01-16 benni } else {
525 11a5e2cf 2024-01-16 benni /*
526 11a5e2cf 2024-01-16 benni * Ok, it wasn't a header. Try to figure out what
527 11a5e2cf 2024-01-16 benni * size records we have.
528 11a5e2cf 2024-01-16 benni */
529 11a5e2cf 2024-01-16 benni result = scorefile_probe(sd);
530 11a5e2cf 2024-01-16 benni if (lseek(sd, 0, SEEK_SET) < 0) {
531 11a5e2cf 2024-01-16 benni warn("Score file %s: lseek", _PATH_SCOREFILE);
532 11a5e2cf 2024-01-16 benni goto sdfail;
533 11a5e2cf 2024-01-16 benni }
534 11a5e2cf 2024-01-16 benni switch (result) {
535 11a5e2cf 2024-01-16 benni case SCOREFILE_CURRENT:
536 11a5e2cf 2024-01-16 benni result = readscores(sd, 0 /* don't flip */);
537 11a5e2cf 2024-01-16 benni break;
538 11a5e2cf 2024-01-16 benni case SCOREFILE_CURRENT_OPP:
539 11a5e2cf 2024-01-16 benni result = readscores(sd, 1 /* do flip */);
540 11a5e2cf 2024-01-16 benni break;
541 11a5e2cf 2024-01-16 benni case SCOREFILE_599:
542 11a5e2cf 2024-01-16 benni result = readscores599(sd, 0 /* don't flip */);
543 11a5e2cf 2024-01-16 benni break;
544 11a5e2cf 2024-01-16 benni case SCOREFILE_599_OPP:
545 11a5e2cf 2024-01-16 benni result = readscores599(sd, 1 /* do flip */);
546 11a5e2cf 2024-01-16 benni break;
547 11a5e2cf 2024-01-16 benni case SCOREFILE_50:
548 11a5e2cf 2024-01-16 benni result = readscores50(sd, 0 /* don't flip */);
549 11a5e2cf 2024-01-16 benni break;
550 11a5e2cf 2024-01-16 benni case SCOREFILE_50_OPP:
551 11a5e2cf 2024-01-16 benni result = readscores50(sd, 1 /* do flip */);
552 11a5e2cf 2024-01-16 benni break;
553 11a5e2cf 2024-01-16 benni default:
554 11a5e2cf 2024-01-16 benni goto sdfail;
555 11a5e2cf 2024-01-16 benni }
556 11a5e2cf 2024-01-16 benni if (result < 0) {
557 11a5e2cf 2024-01-16 benni goto sdfail;
558 11a5e2cf 2024-01-16 benni }
559 11a5e2cf 2024-01-16 benni }
560 11a5e2cf 2024-01-16 benni }
561 11a5e2cf 2024-01-16 benni
562 11a5e2cf 2024-01-16 benni
563 11a5e2cf 2024-01-16 benni if (fdp)
564 11a5e2cf 2024-01-16 benni *fdp = sd;
565 11a5e2cf 2024-01-16 benni else
566 11a5e2cf 2024-01-16 benni close(sd);
567 11a5e2cf 2024-01-16 benni
568 11a5e2cf 2024-01-16 benni return;
569 11a5e2cf 2024-01-16 benni
570 11a5e2cf 2024-01-16 benni sdfail:
571 11a5e2cf 2024-01-16 benni close(sd);
572 11a5e2cf 2024-01-16 benni fail:
573 11a5e2cf 2024-01-16 benni if (fdp != NULL) {
574 11a5e2cf 2024-01-16 benni *fdp = -1;
575 11a5e2cf 2024-01-16 benni }
576 11a5e2cf 2024-01-16 benni nscores = 0;
577 11a5e2cf 2024-01-16 benni }
578 11a5e2cf 2024-01-16 benni
579 11a5e2cf 2024-01-16 benni #ifdef ALLOW_SCORE_UPDATES
580 11a5e2cf 2024-01-16 benni /*
581 11a5e2cf 2024-01-16 benni * Paranoid write wrapper; unlike fwrite() it preserves errno.
582 11a5e2cf 2024-01-16 benni */
583 11a5e2cf 2024-01-16 benni static int
584 11a5e2cf 2024-01-16 benni dowrite(int sd, const void *vbuf, size_t len)
585 11a5e2cf 2024-01-16 benni {
586 11a5e2cf 2024-01-16 benni const char *buf = vbuf;
587 11a5e2cf 2024-01-16 benni ssize_t result;
588 11a5e2cf 2024-01-16 benni size_t done = 0;
589 11a5e2cf 2024-01-16 benni
590 11a5e2cf 2024-01-16 benni while (done < len) {
591 11a5e2cf 2024-01-16 benni result = write(sd, buf+done, len-done);
592 11a5e2cf 2024-01-16 benni if (result < 0) {
593 11a5e2cf 2024-01-16 benni if (errno == EINTR) {
594 11a5e2cf 2024-01-16 benni continue;
595 11a5e2cf 2024-01-16 benni }
596 11a5e2cf 2024-01-16 benni return -1;
597 11a5e2cf 2024-01-16 benni }
598 11a5e2cf 2024-01-16 benni done += result;
599 11a5e2cf 2024-01-16 benni }
600 11a5e2cf 2024-01-16 benni return 0;
601 11a5e2cf 2024-01-16 benni }
602 11a5e2cf 2024-01-16 benni #endif /* ALLOW_SCORE_UPDATES */
603 11a5e2cf 2024-01-16 benni
604 11a5e2cf 2024-01-16 benni /*
605 11a5e2cf 2024-01-16 benni * Write the score file out.
606 11a5e2cf 2024-01-16 benni */
607 11a5e2cf 2024-01-16 benni static void
608 11a5e2cf 2024-01-16 benni putscores(int sd)
609 11a5e2cf 2024-01-16 benni {
610 11a5e2cf 2024-01-16 benni #ifdef ALLOW_SCORE_UPDATES
611 11a5e2cf 2024-01-16 benni struct highscore_header header;
612 11a5e2cf 2024-01-16 benni struct highscore_ondisk buf[MAXHISCORES] = {0};
613 11a5e2cf 2024-01-16 benni int i;
614 11a5e2cf 2024-01-16 benni
615 11a5e2cf 2024-01-16 benni if (sd == -1) {
616 11a5e2cf 2024-01-16 benni return;
617 11a5e2cf 2024-01-16 benni }
618 11a5e2cf 2024-01-16 benni
619 11a5e2cf 2024-01-16 benni memcpy(header.hsh_magic, hsh_magic_val, HSH_MAGIC_SIZE);
620 11a5e2cf 2024-01-16 benni header.hsh_endiantag = HSH_ENDIAN_NATIVE;
621 11a5e2cf 2024-01-16 benni header.hsh_version = HSH_VERSION;
622 11a5e2cf 2024-01-16 benni
623 11a5e2cf 2024-01-16 benni for (i=0; i<nscores; i++) {
624 11a5e2cf 2024-01-16 benni memcpy(buf[i].hso_name, scores[i].hs_name,
625 11a5e2cf 2024-01-16 benni sizeof(buf[i].hso_name));
626 11a5e2cf 2024-01-16 benni buf[i].hso_score = scores[i].hs_score;
627 11a5e2cf 2024-01-16 benni buf[i].hso_level = scores[i].hs_level;
628 11a5e2cf 2024-01-16 benni buf[i].hso_pad = 0xbaadf00d;
629 11a5e2cf 2024-01-16 benni buf[i].hso_time = scores[i].hs_time;
630 11a5e2cf 2024-01-16 benni }
631 11a5e2cf 2024-01-16 benni
632 11a5e2cf 2024-01-16 benni if (lseek(sd, 0, SEEK_SET) < 0) {
633 11a5e2cf 2024-01-16 benni warn("Score file %s: lseek", _PATH_SCOREFILE);
634 11a5e2cf 2024-01-16 benni goto fail;
635 11a5e2cf 2024-01-16 benni }
636 11a5e2cf 2024-01-16 benni if (dowrite(sd, &header, sizeof(header)) < 0 ||
637 11a5e2cf 2024-01-16 benni dowrite(sd, buf, sizeof(buf[0]) * nscores) < 0) {
638 11a5e2cf 2024-01-16 benni warn("Score file %s: write", _PATH_SCOREFILE);
639 11a5e2cf 2024-01-16 benni goto fail;
640 11a5e2cf 2024-01-16 benni }
641 11a5e2cf 2024-01-16 benni return;
642 11a5e2cf 2024-01-16 benni fail:
643 11a5e2cf 2024-01-16 benni warnx("high scores may be damaged");
644 11a5e2cf 2024-01-16 benni #else
645 11a5e2cf 2024-01-16 benni (void)sd;
646 11a5e2cf 2024-01-16 benni #endif /* ALLOW_SCORE_UPDATES */
647 11a5e2cf 2024-01-16 benni }
648 11a5e2cf 2024-01-16 benni
649 11a5e2cf 2024-01-16 benni /*
650 11a5e2cf 2024-01-16 benni * Close the score file.
651 11a5e2cf 2024-01-16 benni */
652 11a5e2cf 2024-01-16 benni static void
653 11a5e2cf 2024-01-16 benni closescores(int sd)
654 11a5e2cf 2024-01-16 benni {
655 11a5e2cf 2024-01-16 benni flock(sd, LOCK_UN);
656 11a5e2cf 2024-01-16 benni close(sd);
657 11a5e2cf 2024-01-16 benni }
658 11a5e2cf 2024-01-16 benni
659 11a5e2cf 2024-01-16 benni /*
660 11a5e2cf 2024-01-16 benni * Read and update the scores file with the current reults.
661 11a5e2cf 2024-01-16 benni */
662 11a5e2cf 2024-01-16 benni void
663 11a5e2cf 2024-01-16 benni savescore(int level)
664 11a5e2cf 2024-01-16 benni {
665 11a5e2cf 2024-01-16 benni struct highscore *sp;
666 11a5e2cf 2024-01-16 benni int i;
667 11a5e2cf 2024-01-16 benni int change;
668 11a5e2cf 2024-01-16 benni int sd;
669 11a5e2cf 2024-01-16 benni const char *me;
670 11a5e2cf 2024-01-16 benni
671 11a5e2cf 2024-01-16 benni getscores(&sd);
672 11a5e2cf 2024-01-16 benni gotscores = 1;
673 11a5e2cf 2024-01-16 benni (void)time(&now);
674 11a5e2cf 2024-01-16 benni
675 11a5e2cf 2024-01-16 benni /*
676 11a5e2cf 2024-01-16 benni * Allow at most one score per person per level -- see if we
677 11a5e2cf 2024-01-16 benni * can replace an existing score, or (easiest) do nothing.
678 11a5e2cf 2024-01-16 benni * Otherwise add new score at end (there is always room).
679 11a5e2cf 2024-01-16 benni */
680 11a5e2cf 2024-01-16 benni change = 0;
681 11a5e2cf 2024-01-16 benni me = thisuser();
682 11a5e2cf 2024-01-16 benni for (i = 0, sp = &scores[0]; i < nscores; i++, sp++) {
683 11a5e2cf 2024-01-16 benni if (sp->hs_level != level || strcmp(sp->hs_name, me) != 0)
684 11a5e2cf 2024-01-16 benni continue;
685 11a5e2cf 2024-01-16 benni if (score > sp->hs_score) {
686 11a5e2cf 2024-01-16 benni (void)printf("%s bettered %s %d score of %d!\n",
687 11a5e2cf 2024-01-16 benni "\nYou", "your old level", level,
688 11a5e2cf 2024-01-16 benni sp->hs_score * sp->hs_level);
689 11a5e2cf 2024-01-16 benni sp->hs_score = score; /* new score */
690 11a5e2cf 2024-01-16 benni sp->hs_time = now; /* and time */
691 11a5e2cf 2024-01-16 benni change = 1;
692 11a5e2cf 2024-01-16 benni } else if (score == sp->hs_score) {
693 11a5e2cf 2024-01-16 benni (void)printf("%s tied %s %d high score.\n",
694 11a5e2cf 2024-01-16 benni "\nYou", "your old level", level);
695 11a5e2cf 2024-01-16 benni sp->hs_time = now; /* renew it */
696 11a5e2cf 2024-01-16 benni change = 1; /* gotta rewrite, sigh */
697 11a5e2cf 2024-01-16 benni } /* else new score < old score: do nothing */
698 11a5e2cf 2024-01-16 benni break;
699 11a5e2cf 2024-01-16 benni }
700 11a5e2cf 2024-01-16 benni if (i >= nscores) {
701 11a5e2cf 2024-01-16 benni strcpy(sp->hs_name, me);
702 11a5e2cf 2024-01-16 benni sp->hs_level = level;
703 11a5e2cf 2024-01-16 benni sp->hs_score = score;
704 11a5e2cf 2024-01-16 benni sp->hs_time = now;
705 11a5e2cf 2024-01-16 benni nscores++;
706 11a5e2cf 2024-01-16 benni change = 1;
707 11a5e2cf 2024-01-16 benni }
708 11a5e2cf 2024-01-16 benni
709 11a5e2cf 2024-01-16 benni if (change) {
710 11a5e2cf 2024-01-16 benni /*
711 11a5e2cf 2024-01-16 benni * Sort & clean the scores, then rewrite.
712 11a5e2cf 2024-01-16 benni */
713 11a5e2cf 2024-01-16 benni nscores = checkscores(scores, nscores);
714 11a5e2cf 2024-01-16 benni putscores(sd);
715 11a5e2cf 2024-01-16 benni }
716 11a5e2cf 2024-01-16 benni closescores(sd);
717 11a5e2cf 2024-01-16 benni }
718 11a5e2cf 2024-01-16 benni
719 11a5e2cf 2024-01-16 benni /*
720 11a5e2cf 2024-01-16 benni * Get login name, or if that fails, get something suitable.
721 11a5e2cf 2024-01-16 benni * The result is always trimmed to fit in a score.
722 11a5e2cf 2024-01-16 benni */
723 11a5e2cf 2024-01-16 benni static char *
724 11a5e2cf 2024-01-16 benni thisuser(void)
725 11a5e2cf 2024-01-16 benni {
726 11a5e2cf 2024-01-16 benni const char *p;
727 11a5e2cf 2024-01-16 benni struct passwd *pw;
728 11a5e2cf 2024-01-16 benni size_t l;
729 11a5e2cf 2024-01-16 benni static char u[sizeof(scores[0].hs_name)];
730 11a5e2cf 2024-01-16 benni
731 11a5e2cf 2024-01-16 benni if (u[0])
732 11a5e2cf 2024-01-16 benni return (u);
733 11a5e2cf 2024-01-16 benni p = getlogin();
734 11a5e2cf 2024-01-16 benni if (p == NULL || *p == '\0') {
735 11a5e2cf 2024-01-16 benni pw = getpwuid(getuid());
736 11a5e2cf 2024-01-16 benni if (pw != NULL)
737 11a5e2cf 2024-01-16 benni p = pw->pw_name;
738 11a5e2cf 2024-01-16 benni else
739 11a5e2cf 2024-01-16 benni p = " ???";
740 11a5e2cf 2024-01-16 benni }
741 11a5e2cf 2024-01-16 benni l = strlen(p);
742 11a5e2cf 2024-01-16 benni if (l >= sizeof(u))
743 11a5e2cf 2024-01-16 benni l = sizeof(u) - 1;
744 11a5e2cf 2024-01-16 benni memcpy(u, p, l);
745 11a5e2cf 2024-01-16 benni u[l] = '\0';
746 11a5e2cf 2024-01-16 benni return (u);
747 11a5e2cf 2024-01-16 benni }
748 11a5e2cf 2024-01-16 benni
749 11a5e2cf 2024-01-16 benni /*
750 11a5e2cf 2024-01-16 benni * Score comparison function for qsort.
751 11a5e2cf 2024-01-16 benni *
752 11a5e2cf 2024-01-16 benni * If two scores are equal, the person who had the score first is
753 11a5e2cf 2024-01-16 benni * listed first in the highscore file.
754 11a5e2cf 2024-01-16 benni */
755 11a5e2cf 2024-01-16 benni static int
756 11a5e2cf 2024-01-16 benni cmpscores(const void *x, const void *y)
757 11a5e2cf 2024-01-16 benni {
758 11a5e2cf 2024-01-16 benni const struct highscore *a, *b;
759 11a5e2cf 2024-01-16 benni long l;
760 11a5e2cf 2024-01-16 benni
761 11a5e2cf 2024-01-16 benni a = x;
762 11a5e2cf 2024-01-16 benni b = y;
763 11a5e2cf 2024-01-16 benni l = (long)b->hs_level * b->hs_score - (long)a->hs_level * a->hs_score;
764 11a5e2cf 2024-01-16 benni if (l < 0)
765 11a5e2cf 2024-01-16 benni return (-1);
766 11a5e2cf 2024-01-16 benni if (l > 0)
767 11a5e2cf 2024-01-16 benni return (1);
768 11a5e2cf 2024-01-16 benni if (a->hs_time < b->hs_time)
769 11a5e2cf 2024-01-16 benni return (-1);
770 11a5e2cf 2024-01-16 benni if (a->hs_time > b->hs_time)
771 11a5e2cf 2024-01-16 benni return (1);
772 11a5e2cf 2024-01-16 benni return (0);
773 11a5e2cf 2024-01-16 benni }
774 11a5e2cf 2024-01-16 benni
775 11a5e2cf 2024-01-16 benni /*
776 11a5e2cf 2024-01-16 benni * If we've added a score to the file, we need to check the file and ensure
777 11a5e2cf 2024-01-16 benni * that this player has only a few entries. The number of entries is
778 11a5e2cf 2024-01-16 benni * controlled by MAXSCORES, and is to ensure that the highscore file is not
779 11a5e2cf 2024-01-16 benni * monopolised by just a few people. People who no longer have accounts are
780 11a5e2cf 2024-01-16 benni * only allowed the highest score. Scores older than EXPIRATION seconds are
781 11a5e2cf 2024-01-16 benni * removed, unless they are someone's personal best.
782 11a5e2cf 2024-01-16 benni * Caveat: the highest score on each level is always kept.
783 11a5e2cf 2024-01-16 benni */
784 11a5e2cf 2024-01-16 benni static int
785 11a5e2cf 2024-01-16 benni checkscores(struct highscore *hs, int num)
786 11a5e2cf 2024-01-16 benni {
787 11a5e2cf 2024-01-16 benni struct highscore *sp;
788 11a5e2cf 2024-01-16 benni int i, j, k, numnames;
789 11a5e2cf 2024-01-16 benni int levelfound[NLEVELS];
790 11a5e2cf 2024-01-16 benni struct peruser {
791 11a5e2cf 2024-01-16 benni char *name;
792 11a5e2cf 2024-01-16 benni int times;
793 11a5e2cf 2024-01-16 benni } count[NUMSPOTS];
794 11a5e2cf 2024-01-16 benni struct peruser *pu;
795 11a5e2cf 2024-01-16 benni
796 11a5e2cf 2024-01-16 benni /*
797 11a5e2cf 2024-01-16 benni * Sort so that highest totals come first.
798 11a5e2cf 2024-01-16 benni *
799 11a5e2cf 2024-01-16 benni * levelfound[i] becomes set when the first high score for that
800 11a5e2cf 2024-01-16 benni * level is encountered. By definition this is the highest score.
801 11a5e2cf 2024-01-16 benni */
802 11a5e2cf 2024-01-16 benni qsort((void *)hs, nscores, sizeof(*hs), cmpscores);
803 11a5e2cf 2024-01-16 benni for (i = MINLEVEL; i < NLEVELS; i++)
804 11a5e2cf 2024-01-16 benni levelfound[i] = 0;
805 11a5e2cf 2024-01-16 benni numnames = 0;
806 11a5e2cf 2024-01-16 benni for (i = 0, sp = hs; i < num;) {
807 11a5e2cf 2024-01-16 benni /*
808 11a5e2cf 2024-01-16 benni * This is O(n^2), but do you think we care?
809 11a5e2cf 2024-01-16 benni */
810 11a5e2cf 2024-01-16 benni for (j = 0, pu = count; j < numnames; j++, pu++)
811 11a5e2cf 2024-01-16 benni if (strcmp(sp->hs_name, pu->name) == 0)
812 11a5e2cf 2024-01-16 benni break;
813 11a5e2cf 2024-01-16 benni if (j == numnames) {
814 11a5e2cf 2024-01-16 benni /*
815 11a5e2cf 2024-01-16 benni * Add new user, set per-user count to 1.
816 11a5e2cf 2024-01-16 benni */
817 11a5e2cf 2024-01-16 benni pu->name = sp->hs_name;
818 11a5e2cf 2024-01-16 benni pu->times = 1;
819 11a5e2cf 2024-01-16 benni numnames++;
820 11a5e2cf 2024-01-16 benni } else {
821 11a5e2cf 2024-01-16 benni /*
822 11a5e2cf 2024-01-16 benni * Two ways to keep this score:
823 11a5e2cf 2024-01-16 benni * - Not too many (per user), still has acct, &
824 11a5e2cf 2024-01-16 benni * score not dated; or
825 11a5e2cf 2024-01-16 benni * - High score on this level.
826 11a5e2cf 2024-01-16 benni */
827 11a5e2cf 2024-01-16 benni if ((pu->times < MAXSCORES &&
828 11a5e2cf 2024-01-16 benni getpwnam(sp->hs_name) != NULL &&
829 11a5e2cf 2024-01-16 benni sp->hs_time + EXPIRATION >= now) ||
830 11a5e2cf 2024-01-16 benni levelfound[sp->hs_level] == 0)
831 11a5e2cf 2024-01-16 benni pu->times++;
832 11a5e2cf 2024-01-16 benni else {
833 11a5e2cf 2024-01-16 benni /*
834 11a5e2cf 2024-01-16 benni * Delete this score, do not count it,
835 11a5e2cf 2024-01-16 benni * do not pass go, do not collect $200.
836 11a5e2cf 2024-01-16 benni */
837 11a5e2cf 2024-01-16 benni num--;
838 11a5e2cf 2024-01-16 benni for (k = i; k < num; k++)
839 11a5e2cf 2024-01-16 benni hs[k] = hs[k + 1];
840 11a5e2cf 2024-01-16 benni continue;
841 11a5e2cf 2024-01-16 benni }
842 11a5e2cf 2024-01-16 benni }
843 11a5e2cf 2024-01-16 benni if (sp->hs_level < NLEVELS && sp->hs_level >= 0)
844 11a5e2cf 2024-01-16 benni levelfound[sp->hs_level] = 1;
845 11a5e2cf 2024-01-16 benni i++, sp++;
846 11a5e2cf 2024-01-16 benni }
847 11a5e2cf 2024-01-16 benni return (num > MAXHISCORES ? MAXHISCORES : num);
848 11a5e2cf 2024-01-16 benni }
849 11a5e2cf 2024-01-16 benni
850 11a5e2cf 2024-01-16 benni /*
851 11a5e2cf 2024-01-16 benni * Show current scores. This must be called after savescore, if
852 11a5e2cf 2024-01-16 benni * savescore is called at all, for two reasons:
853 11a5e2cf 2024-01-16 benni * - Showscores munches the time field.
854 11a5e2cf 2024-01-16 benni * - Even if that were not the case, a new score must be recorded
855 11a5e2cf 2024-01-16 benni * before it can be shown anyway.
856 11a5e2cf 2024-01-16 benni */
857 11a5e2cf 2024-01-16 benni void
858 11a5e2cf 2024-01-16 benni showscores(int level)
859 11a5e2cf 2024-01-16 benni {
860 11a5e2cf 2024-01-16 benni struct highscore *sp;
861 11a5e2cf 2024-01-16 benni int i, n, c;
862 11a5e2cf 2024-01-16 benni const char *me;
863 11a5e2cf 2024-01-16 benni int levelfound[NLEVELS];
864 11a5e2cf 2024-01-16 benni
865 11a5e2cf 2024-01-16 benni if (!gotscores)
866 11a5e2cf 2024-01-16 benni getscores(NULL);
867 11a5e2cf 2024-01-16 benni (void)printf("\n\t\t\t Tetris High Scores\n");
868 11a5e2cf 2024-01-16 benni
869 11a5e2cf 2024-01-16 benni /*
870 11a5e2cf 2024-01-16 benni * If level == 0, the person has not played a game but just asked for
871 11a5e2cf 2024-01-16 benni * the high scores; we do not need to check for printing in highlight
872 11a5e2cf 2024-01-16 benni * mode. If SOstr is null, we can't do highlighting anyway.
873 11a5e2cf 2024-01-16 benni */
874 11a5e2cf 2024-01-16 benni me = level && enter_standout_mode ? thisuser() : NULL;
875 11a5e2cf 2024-01-16 benni
876 11a5e2cf 2024-01-16 benni /*
877 11a5e2cf 2024-01-16 benni * Set times to 0 except for high score on each level.
878 11a5e2cf 2024-01-16 benni */
879 11a5e2cf 2024-01-16 benni for (i = MINLEVEL; i < NLEVELS; i++)
880 11a5e2cf 2024-01-16 benni levelfound[i] = 0;
881 11a5e2cf 2024-01-16 benni for (i = 0, sp = scores; i < nscores; i++, sp++) {
882 11a5e2cf 2024-01-16 benni if (sp->hs_level < NLEVELS && sp->hs_level >= 0) {
883 11a5e2cf 2024-01-16 benni if (levelfound[sp->hs_level])
884 11a5e2cf 2024-01-16 benni sp->hs_time = 0;
885 11a5e2cf 2024-01-16 benni else {
886 11a5e2cf 2024-01-16 benni sp->hs_time = 1;
887 11a5e2cf 2024-01-16 benni levelfound[sp->hs_level] = 1;
888 11a5e2cf 2024-01-16 benni }
889 11a5e2cf 2024-01-16 benni }
890 11a5e2cf 2024-01-16 benni }
891 11a5e2cf 2024-01-16 benni
892 11a5e2cf 2024-01-16 benni /*
893 11a5e2cf 2024-01-16 benni * Page each screenful of scores.
894 11a5e2cf 2024-01-16 benni */
895 11a5e2cf 2024-01-16 benni for (i = 0, sp = scores; i < nscores; sp += n) {
896 11a5e2cf 2024-01-16 benni n = 40;
897 11a5e2cf 2024-01-16 benni if (i + n > nscores)
898 11a5e2cf 2024-01-16 benni n = nscores - i;
899 11a5e2cf 2024-01-16 benni printem(level, i + 1, sp, n, me);
900 11a5e2cf 2024-01-16 benni if ((i += n) < nscores) {
901 11a5e2cf 2024-01-16 benni (void)printf("\nHit RETURN to continue.");
902 11a5e2cf 2024-01-16 benni (void)fflush(stdout);
903 11a5e2cf 2024-01-16 benni while ((c = getchar()) != '\n')
904 11a5e2cf 2024-01-16 benni if (c == EOF)
905 11a5e2cf 2024-01-16 benni break;
906 11a5e2cf 2024-01-16 benni (void)printf("\n");
907 11a5e2cf 2024-01-16 benni }
908 11a5e2cf 2024-01-16 benni }
909 11a5e2cf 2024-01-16 benni }
910 11a5e2cf 2024-01-16 benni
911 11a5e2cf 2024-01-16 benni static void
912 11a5e2cf 2024-01-16 benni printem(int level, int offset, struct highscore *hs, int n, const char *me)
913 11a5e2cf 2024-01-16 benni {
914 11a5e2cf 2024-01-16 benni struct highscore *sp;
915 11a5e2cf 2024-01-16 benni int nrows, row, col, item, i, highlight;
916 11a5e2cf 2024-01-16 benni char buf[100];
917 11a5e2cf 2024-01-16 benni #define TITLE "Rank Score Name (points/level)"
918 11a5e2cf 2024-01-16 benni
919 11a5e2cf 2024-01-16 benni /*
920 11a5e2cf 2024-01-16 benni * This makes a nice two-column sort with headers, but it's a bit
921 11a5e2cf 2024-01-16 benni * convoluted...
922 11a5e2cf 2024-01-16 benni */
923 11a5e2cf 2024-01-16 benni printf("%s %s\n", TITLE, n > 1 ? TITLE : "");
924 11a5e2cf 2024-01-16 benni
925 11a5e2cf 2024-01-16 benni highlight = 0;
926 11a5e2cf 2024-01-16 benni nrows = (n + 1) / 2;
927 11a5e2cf 2024-01-16 benni
928 11a5e2cf 2024-01-16 benni for (row = 0; row < nrows; row++) {
929 11a5e2cf 2024-01-16 benni for (col = 0; col < 2; col++) {
930 11a5e2cf 2024-01-16 benni item = col * nrows + row;
931 11a5e2cf 2024-01-16 benni if (item >= n) {
932 11a5e2cf 2024-01-16 benni /*
933 11a5e2cf 2024-01-16 benni * Can only occur on trailing columns.
934 11a5e2cf 2024-01-16 benni */
935 11a5e2cf 2024-01-16 benni (void)putchar('\n');
936 11a5e2cf 2024-01-16 benni continue;
937 11a5e2cf 2024-01-16 benni }
938 11a5e2cf 2024-01-16 benni sp = &hs[item];
939 11a5e2cf 2024-01-16 benni (void)snprintf(buf, sizeof(buf),
940 11a5e2cf 2024-01-16 benni "%3d%c %6d %-11s (%6d on %d)",
941 11a5e2cf 2024-01-16 benni item + offset, sp->hs_time ? '*' : ' ',
942 11a5e2cf 2024-01-16 benni sp->hs_score * sp->hs_level,
943 11a5e2cf 2024-01-16 benni sp->hs_name, sp->hs_score, sp->hs_level);
944 11a5e2cf 2024-01-16 benni /*
945 11a5e2cf 2024-01-16 benni * Highlight if appropriate. This works because
946 11a5e2cf 2024-01-16 benni * we only get one score per level.
947 11a5e2cf 2024-01-16 benni */
948 11a5e2cf 2024-01-16 benni if (me != NULL &&
949 11a5e2cf 2024-01-16 benni sp->hs_level == level &&
950 11a5e2cf 2024-01-16 benni sp->hs_score == score &&
951 11a5e2cf 2024-01-16 benni strcmp(sp->hs_name, me) == 0) {
952 11a5e2cf 2024-01-16 benni putpad(enter_standout_mode);
953 11a5e2cf 2024-01-16 benni highlight = 1;
954 11a5e2cf 2024-01-16 benni }
955 11a5e2cf 2024-01-16 benni (void)printf("%s", buf);
956 11a5e2cf 2024-01-16 benni if (highlight) {
957 11a5e2cf 2024-01-16 benni putpad(exit_standout_mode);
958 11a5e2cf 2024-01-16 benni highlight = 0;
959 11a5e2cf 2024-01-16 benni }
960 11a5e2cf 2024-01-16 benni
961 11a5e2cf 2024-01-16 benni /* fill in spaces so column 1 lines up */
962 11a5e2cf 2024-01-16 benni if (col == 0)
963 11a5e2cf 2024-01-16 benni for (i = 40 - strlen(buf); --i >= 0;)
964 11a5e2cf 2024-01-16 benni (void)putchar(' ');
965 11a5e2cf 2024-01-16 benni else /* col == 1 */
966 11a5e2cf 2024-01-16 benni (void)putchar('\n');
967 11a5e2cf 2024-01-16 benni }
968 11a5e2cf 2024-01-16 benni }
969 11a5e2cf 2024-01-16 benni }