code cleanup: use terniaries
[netris.git] / util.c
1 /*
2  * Netris -- A free networked version of T*tris
3  * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 #include "netris.h"
21 #include <stdarg.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/time.h>
27 #include <netdb.h>
28 #include <errno.h>
29 #include <setjmp.h>
30
31 #include "util.h"
32
33 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event);
34 static EventGenRec alarmGen = {
35         &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm
36 };
37 static EventGenRec *nextGen = &alarmGen;
38
39 static int myRandSeed = 1;
40
41 static long baseTimeval;
42
43
44 void AtExit(void (*handler)(void))
45 {
46 #ifdef HAS_ON_EXIT
47         on_exit((void *)handler, NULL);
48 #else
49         atexit(handler);
50 #endif
51 }
52
53 ///////////// HELP MESSAGES /////////////
54
55 void Header(void)
56 {
57         fprintf(stderr,
58                 "NETRIS %s\t(c) 1994-1996,1999 Mark H. Weaver <mhw@netris.org>\n"
59                 "          \t(c) 2002 Shiar <shiar@shiar.org>\n\n",
60                 version_string
61         );
62 }
63
64 void Usage(void)
65 {
66         Header();
67         fprintf(stderr,
68                 "Usage: netris <options>\n"
69                 "\n"
70                 "  -h, --help\t\tPrint this usage information\n"
71                 "  -H, --info\t\tShow distribution and warranty information\n"
72                 "  -R, --rules\t\tShow game rules\n"
73                 "\n"
74                 "  -S, --slowterm\tDisable inverse/bold/color for slow terminals\n"
75                 "  -a, --ascii\t\tUse ascii characters\n"
76                 "  -C, --color=0\t\tDisable color\n"
77                 "\n"
78                 "  -c, --connect <host>\tInitiate connection\n"
79                 "  -p, --port <port>\tSet port number (default is %d)\n"
80                 "\n"
81                 "  -t, --team <team>\tJoin a team (don't receive lines from your teammates)\n"
82                 "  -l, --level <lvl>\tBegin at a higher level (can be used as handicap)\n"
83                 "  -k, --keys <keys>\tRemap keys (default is \"%s\" for cursors)\n"
84                 "  -d, --dropmode\tDrops go into drop mode\n"
85                 "  -D, --instadrop\tInstant drop\n"
86                 "\n"
87                 "  -r, --robot <cmd>\tExecute program to control the game instead of keyboard\n"
88                 "  -F, --fair-robot\tUse fair robot interface\n"
89                 "\n", DEFAULT_PORT, DEFAULT_KEYS
90         );
91 }
92
93 void DistInfo(void)
94 {
95         fprintf(stderr,
96                 "This program is free software; you can redistribute it and/or modify\n"
97                 "it under the terms of the GNU General Public License as published by\n"
98                 "the Free Software Foundation; either version 2 of the License, or\n"
99                 "(at your option) any later version.\n"
100                 "\n"
101                 "This program is distributed in the hope that it will be useful,\n"
102                 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
103                 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
104                 "GNU General Public License for more details.\n"
105                 "\n"
106                 "You should have received a copy of the GNU General Public License\n"
107                 "along with this program; if not, write to the Free Software\n"
108                 "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
109                 "\n"
110         );
111 }
112
113 void Rules(void)
114 {
115         Header();
116         fprintf(stderr,
117                 "One player mode\n"
118                 "---------------\n"
119                 "Good old Tetris. Scoring is like on the GameBoy version (so try to\n"
120                 "remove as many lines at once as you can). After removing ten lines\n"
121                 "you go to the next level, which will be faster thus making the game\n"
122                 "harder to play.\n"
123                 "\n"
124                 "Two player mode\n"
125                 "---------------\n"
126                 "It's just like normal T*tris except that when you clear more than\n"
127                 "one row with a single piece, the other player receives penalty lines\n"
128                 "For clearing 2, 3 or 4 rows, respectively 1, 2 or 4 junk rows will\n"
129                 "be added to the bottom of your opponent's board respectively.\n"
130                 "The junk rows have exactly one empty column, which will line up for\n"
131                 "multiple rows.\n"
132                 "\n"
133                 "The longest surviving player wins the game.\n"
134                 "\n"
135         );
136 }
137
138 ///////////// RANDOM /////////////
139
140 /*
141  * My really crappy random number generator follows
142  * Should be more than sufficient for our purposes though
143  */
144 void SRandom(int seed)
145 {
146         Game.seed = seed;
147         myRandSeed = seed % 31751 + 1;
148 }
149
150 int Random(int min, int max1)
151 { //return a random value
152         myRandSeed = (myRandSeed * 31751 + 15437) % 32767;
153         return myRandSeed % (max1 - min) + min;
154 }
155
156 ///////////// I/O /////////////
157
158 int MyRead(int fd, void *data, int len)
159 {
160         int result, left;
161
162         left = len;
163         while (left > 0) {
164                 result = read(fd, data, left);
165                 if (result > 0) {
166                         data = ((char *)data) + result;
167                         left -= result;
168                 }
169                 else if (errno != EINTR)
170                         return result;
171         }
172         return len;
173 }
174
175 int MyWrite(int fd, void *data, int len)
176 {
177         int result, left;
178
179         left = len;
180         while (left > 0) {
181                 result = write(fd, data, left);
182                 if (result > 0) {
183                         data = ((char *)data) + result;
184                         left -= result;
185                 }
186                 else if (errno != EINTR)
187                         return result;
188         }
189         return len;
190 }
191
192 ///////////// TIME /////////////
193
194 void NormalizeTime(struct timeval *tv)
195 {
196         tv->tv_sec += tv->tv_usec / 1000000;
197         tv->tv_usec %= 1000000;
198         if (tv->tv_usec < 0) {
199                 tv->tv_usec += 1000000;
200                 --tv->tv_sec;
201         }
202 }
203
204 void CatchAlarm(int sig)
205 {
206         alarmGen.ready = 1;
207         signal(SIGALRM, CatchAlarm);
208 }
209
210 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
211 {
212         return E_alarm;
213 }
214
215 void SetTimeval(struct timeval *tv, long usec)
216 {
217         tv->tv_sec = usec / 1000000;
218         tv->tv_usec = usec % 1000000;
219 }
220
221 long GetTimeval(struct timeval *tv)
222 {
223         return tv->tv_sec * 1000000 + tv->tv_usec;
224 }
225
226 long AbsTimeval(void)
227 {
228         struct timeval tv;
229
230         gettimeofday(&tv, NULL);
231         return GetTimeval(&tv);
232 }
233
234 void ResetBaseTime(void)
235 { //Reset the timer
236         baseTimeval = AbsTimeval();
237 }
238
239 void PauseTime(void)
240 { //Pause the timer
241         baseTimeval -= AbsTimeval();
242 }
243
244 void ResumeTime(void)
245 { //Unpause timer
246         baseTimeval += AbsTimeval();
247 }
248
249 long CurTimeval(void)
250 {
251         struct timeval tv;
252
253         gettimeofday(&tv, NULL);
254         return GetTimeval(&tv) - baseTimeval;
255 }
256
257 static long SetITimer1(long interval, long value)
258 {
259         struct itimerval it, old;
260
261         SetTimeval(&it.it_interval, interval);
262         SetTimeval(&it.it_value, value);
263         if (setitimer(ITIMER_REAL, &it, &old) < 0)
264                 die("setitimer");
265         signal(SIGALRM, CatchAlarm);
266         return GetTimeval(&old.it_value);
267 }
268
269 long SetITimer(long interval, long value)
270 {
271         long old;
272
273         old = SetITimer1(0, 0);
274         alarmGen.ready = 0;
275         SetITimer1(interval, value);
276         return old;
277 }
278
279 ///////////// ... /////////////
280
281 volatile void die(char *msg)
282 {
283         perror(msg);
284         exit(1);
285 }
286
287 volatile void fatal(char *msg)
288 {
289         fprintf(stderr, "%s\n", msg);
290         exit(1);
291 }
292
293 void BlockSignals(MySigSet *saved, ...)
294 {
295         MySigSet set;
296         va_list args;
297         int sig;
298
299         va_start(args, saved);
300 #ifdef HAS_SIGPROCMASK
301         sigemptyset(&set);
302 #else
303         set = 0;
304 #endif
305         while ((sig = va_arg(args, int))) {
306 #ifdef HAS_SIGPROCMASK
307                 sigaddset(&set, sig);
308 #else
309                 sig |= sigmask(sig);
310 #endif
311         }
312 #ifdef HAS_SIGPROCMASK
313         sigprocmask(SIG_BLOCK, &set, saved);
314 #else
315         *saved = sigblock(set);
316 #endif
317         va_end(args);
318 }
319
320 void RestoreSignals(MySigSet *saved, MySigSet *set)
321 {
322 #ifdef HAS_SIGPROCMASK
323         sigprocmask(SIG_SETMASK, set, saved);
324 #else
325         if (saved)
326                 *saved = sigsetmask(*set);
327         else
328                 sigsetmask(*set);
329 #endif
330 }
331
332 ///////////// EVENTS /////////////
333
334 void AddEventGen(EventGenRec *gen)
335 {
336         assert(gen->next == NULL);
337         gen->next = nextGen->next;
338         nextGen->next = gen;
339 }
340
341 void RemoveEventGen(EventGenRec *gen)
342 {
343         // assert(gen->next != NULL);   /* Be more forgiving, for SIGINTs */
344         if (gen->next) {
345                 while (nextGen->next != gen)
346                         nextGen = nextGen->next;
347                 nextGen->next = gen->next;
348                 gen->next = NULL;
349         }
350 }
351
352 MyEventType WaitMyEvent(MyEvent *event, int mask)
353 { //poll
354         int i, retry = 0;
355         fd_set fds[FT_len];
356         EventGenRec *gen;
357         int result, anyReady, anySet;
358         struct timeval tv;
359
360         for (;;) {
361                 for (i = 0; i < FT_len; ++i)
362                         FD_ZERO(&fds[i]);
363                 anyReady = anySet = 0;
364                 gen = nextGen;
365                 do {
366                         if (gen->mask & mask) {
367                                 if (gen->ready)
368                                         anyReady = 1;
369                                 if (gen->fd >= 0) {
370                                         FD_SET(gen->fd, &fds[gen->fdType]);
371                                         anySet = 1;
372                                 }
373                         }
374                         gen = gen->next;
375                 } while (gen != nextGen);
376                 if (anySet) {
377                         tv.tv_sec = 0;
378                         tv.tv_usec = (retry && !anyReady) ? 500000 : 0;
379                         result = select(FD_SETSIZE, &fds[FT_read], &fds[FT_write],
380                                         &fds[FT_except], anyReady ? &tv : NULL);
381                 }
382                 else {
383                         if (retry && !anyReady)
384                                 sleep(1);
385                         result = 0;
386                 }
387                 gen = nextGen;
388                 do {
389                         if ((gen->mask & mask) && (gen->ready || (
390                                 result > 0 && gen->fd >= 0
391                                 && FD_ISSET(gen->fd, &fds[gen->fdType])
392                         ))) {
393                                 gen->ready = 0;
394                                 event->type = gen->func(gen, event);
395                                 if (event->type != E_none) {
396                                         nextGen = gen->next;
397                                         return event->type;
398                                 }
399                         }
400                         gen = gen->next;
401                 } while (gen != nextGen);
402                 retry = 1;
403         }
404 }
405