Fix replay time in practice mode
[neverball] / ball / demo.c
1 /*   
2  * Copyright (C) 2003 Robert Kooima
3  *
4  * NEVERBALL is  free software; you can redistribute  it and/or modify
5  * it under the  terms of the GNU General  Public License as published
6  * by the Free  Software Foundation; either version 2  of the License,
7  * or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
11  * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
12  * General Public License for more details.
13  */
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 #ifndef _WIN32
20 #include <unistd.h>
21 #endif
22
23 #include "set.h"
24 #include "demo.h"
25 #include "game.h"
26 #include "level.h"
27 #include "audio.h"
28 #include "solid.h"
29 #include "config.h"
30 #include "binary.h"
31
32 /*---------------------------------------------------------------------------*/
33
34 #define MAGIC 0x4E425251
35 #define DEMO_FPS_CAP 200 /* FPS replay limit, keeps size down on monster systems */
36
37 static FILE *demo_fp;
38
39 static char name[MAXDEMO][MAXNAM];
40 static char shot[MAXDEMO][PATHMAX];
41 static int  score[MAXDEMO];
42 static int  timer[MAXDEMO];
43 static int  count;
44
45 /*---------------------------------------------------------------------------*/
46 #ifdef _WIN32
47
48 int demo_scan(void)
49 {
50     WIN32_FIND_DATA d;
51     HANDLE h;
52     FILE *fp;
53
54     count = 0;
55
56     /* Scan the user directory for files. */
57
58     if ((h = FindFirstFile(config_user("*"), &d)) != INVALID_HANDLE_VALUE)
59     {
60         do
61             if ((fp = fopen(config_user(d.cFileName), FMODE_RB)))
62             {
63                 int magic;
64                 int t, c;
65
66                 /* Note the name and screen shot of each replay file. */
67
68                 get_index(fp, &magic);
69                 get_index(fp, &t);
70                 get_index(fp, &c);
71
72                 if (magic == MAGIC && t)
73                 {
74                     fread(shot[count], 1, PATHMAX, fp);
75                     timer[count] = t;
76                     score[count] = c;
77                     strncpy(name[count], d.cFileName, MAXNAM);
78                     count++;
79                 }
80                 fclose(fp);
81             }
82         while (count < MAXDEMO && FindNextFile(h, &d));
83
84         FindClose(h);
85     }
86     return count;
87 }
88
89 #else /* _WIN32 */
90 #include <dirent.h>
91
92 int demo_scan(void)
93 {
94     struct dirent *ent;
95     FILE *fp;
96     DIR  *dp;
97
98     count = 0;
99
100     /* Scan the user directory for files. */
101
102     if ((dp = opendir(config_user(""))))
103     {
104         while (count < MAXDEMO && (ent = readdir(dp)))
105             if ((fp = fopen(config_user(ent->d_name), FMODE_RB)))
106             {
107                 int magic;
108                 int t, c;
109
110                 /* Note the name and screen shot of each replay file. */
111
112                 get_index(fp, &magic);
113                 get_index(fp, &t);
114                 get_index(fp, &c);
115
116                 if (magic == MAGIC && t)
117                 {
118                     fread(shot[count], 1, PATHMAX, fp);
119                     timer[count] = t;
120                     score[count] = c;
121                     strncpy(name[count], ent->d_name, MAXNAM);
122                     count++;
123                 }
124                 fclose(fp);
125             }
126
127         closedir(dp);
128     }
129     return count;
130 }
131 #endif /* _WIN32 */
132
133 const char *demo_pick(void)
134 {
135     int n = demo_scan();
136
137     return (n > 0) ? name[(rand() >> 4) % n] : NULL;
138 }
139
140 const char *demo_name(int i)
141 {
142     return (0 <= i && i < count) ? name[i] : NULL;
143 }
144
145 const char *demo_shot(int i)
146 {
147     return (0 <= i && i < count) ? shot[i] : NULL;
148 }
149
150 int demo_coins(int i)
151 {
152     return (0 <= i && i < count) ? score[i] : 0;
153 }
154
155 int demo_clock(int i)
156 {
157     return (0 <= i && i < count) ? timer[i] : 0;
158 }
159
160 /*---------------------------------------------------------------------------*/
161
162 int demo_exists(char *name)
163 {
164     FILE *fp;
165
166     if ((fp = fopen(config_user(name), "r")))
167     {
168         fclose(fp);
169         return 1;
170     }
171     return 0;
172 }
173
174 void demo_unique(char *name)
175 {
176     int i;
177
178     /* Generate a unique name for a new replay save. */
179
180     for (i = 1; i < 100; i++)
181     {
182         sprintf(name, _("replay%02d"), i);
183
184         if (!demo_exists(name))
185             return;
186     }
187 }
188
189 /*---------------------------------------------------------------------------*/
190
191 int demo_play_init(const char *name,
192                    const char *file,
193                    const char *back,
194                    const char *grad,
195                    const char *song,
196                    const char *shot,
197                    int t, int g, int s, int c, int b)
198 {
199     int magic = MAGIC;
200     int zero  = 0;
201     int st    = t;
202     int sg    = g;
203     int ss    = s;
204     int sc    = c;
205     int sb    = b;
206
207     /* Initialize the replay file.  Write the header. */
208
209     if (name && (demo_fp = fopen(config_user(name), FMODE_WB)))
210     {
211         put_index(demo_fp, &magic);
212         put_index(demo_fp, &zero);
213         put_index(demo_fp, &zero);
214
215         fwrite(shot, 1, PATHMAX, demo_fp);
216         fwrite(file, 1, PATHMAX, demo_fp);
217         fwrite(back, 1, PATHMAX, demo_fp);
218         fwrite(grad, 1, PATHMAX, demo_fp);
219         fwrite(song, 1, PATHMAX, demo_fp);
220
221         put_index(demo_fp, &st);
222         put_index(demo_fp, &sg);
223         put_index(demo_fp, &ss);
224         put_index(demo_fp, &sc);
225         put_index(demo_fp, &sb);
226
227         audio_music_fade_to(2.0f, song);
228
229         return game_init(file, back, grad, t, (g == 0));
230     }
231     return 0;
232 }
233
234 void demo_play_step(float dt)
235 {
236     static float fps_track = 0.0f;
237     static float fps_cap   = 1.0f / (float) DEMO_FPS_CAP;
238
239     if (demo_fp)
240     {
241         fps_track += dt;
242         if (fps_track > fps_cap)
243         {            
244             put_float(demo_fp, &fps_track);
245             put_game_state(demo_fp); 
246             fps_track = 0.0f;
247         }
248     }
249 }
250
251 void demo_play_stat(int coins, int timer)
252 {
253     int c = coins;
254     int t = timer;
255
256     if (demo_fp)
257     {
258         /* Update the demo header using the final score and time. */
259
260         fseek(demo_fp, 4, SEEK_SET);
261
262         put_index(demo_fp, &t);
263         put_index(demo_fp, &c);
264
265         fseek(demo_fp, 0, SEEK_END);
266     }
267 }
268
269 void demo_play_stop(const char *name)
270 {
271     char src[PATHMAX];
272     char dst[PATHMAX];
273
274     if (demo_fp)
275     {
276         fclose(demo_fp);
277         demo_fp = NULL;
278
279         /* Rename the temporary replay file to its given name. */
280
281         if (name)
282         {
283             strncpy(src, config_user(USER_REPLAY_FILE), PATHMAX);
284             strncpy(dst, config_user(name),             PATHMAX);
285
286             rename(src, dst);
287         }
288     }
289 }
290
291 /*---------------------------------------------------------------------------*/
292
293 static char demo_replay_name[MAXSTR];
294
295 int demo_replay_init(const char *name, int *s, int *c, int *b, int *g, int *t)
296 {
297     char shot[PATHMAX];
298     char file[PATHMAX];
299     char back[PATHMAX];
300     char grad[PATHMAX];
301     char song[PATHMAX];
302
303     int magic;
304     int zero;
305     int st;
306     int sg;
307     int ss;
308     int sc;
309     int sb;
310     
311     if ((demo_fp = fopen(config_user(name), FMODE_RB)))
312     {
313         strncpy(demo_replay_name, name, MAXSTR);
314
315         get_index(demo_fp, &magic);
316
317         if (magic == MAGIC)
318         {
319             get_index(demo_fp, &zero);
320             get_index(demo_fp, &zero);
321
322             fread(shot, 1, PATHMAX, demo_fp);
323             fread(file, 1, PATHMAX, demo_fp);
324             fread(back, 1, PATHMAX, demo_fp);
325             fread(grad, 1, PATHMAX, demo_fp);
326             fread(song, 1, PATHMAX, demo_fp);
327
328             get_index(demo_fp, &st);
329             get_index(demo_fp, &sg);
330             get_index(demo_fp, &ss);
331             get_index(demo_fp, &sc);
332             get_index(demo_fp, &sb);
333
334             if (g) *g = (int) sg;
335             if (s) *s = (int) ss;
336             if (c) *c = (int) sc;
337             if (b) *b = (int) sb;
338             if (t) *t = (int) st;
339
340             if (g)
341             {
342                 audio_music_fade_to(0.5f, song);
343                 return game_init(file, back, grad, st, (*g == 0));
344             }
345             else
346                 return game_init(file, back, grad, st, 1);
347         }
348         fclose(demo_fp);
349     }
350     return 0;
351 }
352
353 int demo_replay_step(float *dt)
354 {
355     const float g[3] = { 0.0f, -9.8f, 0.0f };
356
357     if (demo_fp)
358     {
359         get_float(demo_fp, dt);
360
361         if (feof(demo_fp) == 0)
362         {
363             /* Play out current game state for particles, clock, etc. */
364
365             game_step(g, *dt, 1);
366
367             /* Load real current game state from file. */
368             
369             if (get_game_state(demo_fp))
370                 return 1;
371         }
372     }
373     return 0;
374 }
375
376 void demo_replay_stop(int d)
377 {
378     if (demo_fp)
379     {
380         fclose(demo_fp);
381         demo_fp = NULL;
382
383         if (d) unlink(config_user(demo_replay_name));
384     }
385 }
386
387 /*---------------------------------------------------------------------------*/