Renamed a variable in parse_args from not_miss to missing and changed code
[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 #include <time.h>
19
20 #ifndef _WIN32
21 #include <unistd.h>
22 #endif
23
24 #include "demo.h"
25 #include "game.h"
26 #include "audio.h"
27 #include "solid.h"
28 #include "config.h"
29 #include "binary.h"
30
31 /*---------------------------------------------------------------------------*/
32
33 #define MAGIC     0x4E425251 /* Replay file magic number (should not change) */
34 #define OLD_MAGIC 0x4E425250 /* Replay file magic number for neverball 1.4.0 */
35 #define REPLAY_VERSION  1    /* Replay file format version (can change)      */
36
37 #define DEMO_FPS_CAP 200 /* FPS replay limit, keeps size down on monster systems */
38
39 static FILE *demo_fp;
40
41 static struct demo demos[MAXDEMO]; /* Array of scanned demos */
42
43 static int count; /* number of scanned demos */
44
45 /*---------------------------------------------------------------------------*/
46
47 void demo_dump_info(const struct demo *d)
48 /* This function dump the info of a demo structure
49  * It's only a function for debugging, no need of I18N */
50 {
51     printf("Name:         %s\n"
52            "File:         %s\n"
53            "NB Version:   %s\n"
54            "Time:         %d\n"
55            "Coins:        %d\n"
56            "Mode:         %d\n"
57            "State:        %d\n"
58            "Date:         %s"
59            "Player:       %s\n"
60            "Shot:         %s\n"
61            "Level:        %s\n"
62            "  Back:       %s\n"
63            "  Grad:       %s\n"
64            "  Song:       %s\n"
65            "Time:         %d\n"
66            "Goal:         %d\n"
67            "Score:        %d\n"
68            "Balls:        %d\n"
69            "Total Time:   %d\n",
70            d->name, d->filename,
71            d->nb_version,
72            d->timer, d->coins, d->mode, d->state, ctime(&d->date),
73            d->player,
74            d->shot, d->file, d->back, d->grad, d->song,
75            d->time, d->goal, d->score, d->balls, d->times);
76 }
77
78 FILE *demo_header_read(const char *filename, struct demo *d)
79 /* Open a demo file, fill the demo information structure
80  * If success, return the file pointer positioned after the header
81  * If fail, return null 
82  */
83 {
84     FILE *fp;
85     char *basename;
86     char buf[MAXSTR];
87     
88     if ((fp = fopen(filename, FMODE_RB)))
89     {
90         int magic;
91         int version;
92         int t;
93
94         get_index(fp, &magic);
95         get_index(fp, &version);
96
97         /* if time is 0, it means the replay was not finished */
98         get_index(fp, &t);
99
100         if (magic == MAGIC && version == REPLAY_VERSION && t)
101         {
102             d->timer = t;
103             strncpy(d->filename, filename, PATHMAX);
104             
105             /* Remove the directory delimiter */
106 #ifdef _WIN32
107             basename = strrchr(filename, '\\');
108 #else
109             basename = strrchr(filename, '/');
110 #endif
111             
112             if (basename != NULL)
113                 strncpy(buf, basename + 1, MAXSTR);
114             else
115                 strncpy(buf, filename, MAXSTR);
116             
117             /* Remove the extension */
118             t = strlen(buf) - strlen(REPLAY_EXT);
119             if ((t > 1) && (strcmp(buf + t, REPLAY_EXT) == 0))
120                 buf[t] = '\0';
121             strncpy(d->name, buf, PATHMAX);
122             d->name[PATHMAX - 1] = '\0';
123             
124             get_index (fp, &d->coins);
125             get_index (fp, &d->state);
126             get_index (fp, &d->mode);
127             get_index (fp, (int *)&d->date);
128             get_string(fp, d->player, MAXNAM);
129             get_string(fp, d->shot,   PATHMAX);
130             get_string(fp, d->file,   PATHMAX);
131             get_string(fp, d->back,   PATHMAX);
132             get_string(fp, d->grad,   PATHMAX);
133             get_string(fp, d->song,   PATHMAX);
134             get_index (fp, &d->time);
135             get_index (fp, &d->goal);
136             get_index (fp, &d->score);
137             get_index (fp, &d->balls);
138             get_index (fp, &d->times);
139             get_string(fp, d->nb_version, 20);
140
141             return fp;
142         }
143         fclose(fp);
144     }
145     return NULL;
146 }
147
148 static FILE *demo_header_write(struct demo *d)
149 /* Create a new demo file, write the demo information structure
150  * If success, return the file pointer positioned after the header
151  * If fail, return null
152  * */
153 {
154     int magic = MAGIC;
155     int version = REPLAY_VERSION;
156     int zero  = 0;
157     FILE *fp;
158     
159     if (d->filename && (fp = fopen(d->filename, FMODE_WB)))
160     {
161         put_index (fp, &magic);
162         put_index (fp, &version);
163         put_index (fp, &zero);
164         put_index (fp, &zero);
165         put_index (fp, &zero);
166         put_index (fp, &d->mode);
167         put_index (fp, (int *)&d->date);
168         put_string(fp, d->player);
169         put_string(fp, d->shot);
170         put_string(fp, d->file);
171         put_string(fp, d->back);
172         put_string(fp, d->grad);
173         put_string(fp, d->song);
174         put_index (fp, &d->time);
175         put_index (fp, &d->goal);
176         put_index (fp, &d->score);
177         put_index (fp, &d->balls);
178         put_index (fp, &d->times);
179         put_string(fp, VERSION);
180
181         return fp;
182     }
183     return NULL;
184 }
185
186 void demo_header_stop(FILE *fp, int coins, int timer, int state)
187 /* Update the demo header using the final level state. */
188 {
189     long pos = ftell(fp);
190     fseek(fp, 8, SEEK_SET);
191     put_index(fp, &timer);
192     put_index(fp, &coins);
193     put_index(fp, &state);
194     fseek(fp, pos, SEEK_SET);
195 }
196         
197 /*---------------------------------------------------------------------------*/
198
199 static void demo_scan_file(const char *filename)
200 /* Scan another file (used by demo_scan */
201 {
202     FILE *fp;
203     if ((fp = demo_header_read(config_user(filename), &demos[count])))
204     {
205         count++;
206         fclose(fp);
207     }
208 }
209
210 #ifdef _WIN32
211
212 int demo_scan(void)
213 {
214     WIN32_FIND_DATA d;
215     HANDLE h;
216
217     count = 0;
218
219     /* Scan the user directory for files. */
220
221     if ((h = FindFirstFile(config_user("*"), &d)) != INVALID_HANDLE_VALUE)
222     {
223         do
224             demo_scan_file(d.cFileName);
225         while (count < MAXDEMO && FindNextFile(h, &d));
226
227         FindClose(h);
228     }
229     return count;
230 }
231
232 #else /* _WIN32 */
233 #include <dirent.h>
234
235 int demo_scan(void)
236 {
237     struct dirent *ent;
238     DIR  *dp;
239
240     count = 0;
241
242     /* Scan the user directory for files. */
243
244     if ((dp = opendir(config_user(""))))
245     {
246         while (count < MAXDEMO && (ent = readdir(dp)))
247             demo_scan_file(ent->d_name);
248
249         closedir(dp);
250     }
251     return count;
252 }
253 #endif /* _WIN32 */
254
255 const char *demo_pick(void)
256 {
257     int n = demo_scan();
258
259     return (n > 0) ? demos[(rand() >> 4) % n].filename : NULL;
260 }
261
262 const struct demo *get_demo(int i)
263 {
264     return (0 <= i && i < count) ? &demos[i] : NULL;
265 }
266
267 const char *date_to_str(time_t i)
268 {
269     static char str[MAXSTR];
270     struct tm *tm = localtime(&i);
271     strftime (str, MAXSTR, "%c", tm);
272     return str;
273 }
274
275 /*---------------------------------------------------------------------------*/
276
277 int demo_exists(char *name)
278 {
279     FILE *fp;
280     char buf[MAXSTR];
281
282     strcpy(buf, config_user(name));
283     strcat(buf, REPLAY_EXT);
284     if ((fp = fopen(buf, "r")))
285     {
286         fclose(fp);
287         return 1;
288     }
289     return 0;
290 }
291
292 void demo_unique(char *name)
293 {
294     int i;
295
296     /* Generate a unique name for a new replay save. */
297
298     for (i = 1; i < 100; i++)
299     {
300         sprintf(name, _("replay%02d"), i);
301
302         if (!demo_exists(name))
303             return;
304     }
305 }
306
307 /*---------------------------------------------------------------------------*/
308
309 int demo_play_init(const char *name,
310                    const struct level *level,
311                    const struct level_game *lg)
312 {
313     struct demo demo;
314
315     /* file structure */
316     strncpy(demo.name, name, MAXNAM);
317     strncpy(demo.filename, config_user(name), PATHMAX);
318     strcat(demo.filename, REPLAY_EXT);
319     demo.time = demo.coins = demo.state = 0;
320     demo.mode = lg->mode;
321     demo.date = time(NULL);
322     config_get_s(CONFIG_PLAYER, demo.player, MAXNAM);
323     strncpy(demo.shot, level->shot, PATHMAX);
324     strncpy(demo.file, level->file, PATHMAX);
325     strncpy(demo.back, level->back, PATHMAX);
326     strncpy(demo.grad, level->grad, PATHMAX);
327     strncpy(demo.song, level->song, PATHMAX);
328     demo.time  = lg->time;
329     demo.goal  = lg->goal;
330     demo.score = lg->score;
331     demo.balls = lg->balls;
332     demo.times = lg->times;
333
334     demo_fp = demo_header_write(&demo);
335     if (demo_fp == NULL)
336         return 0;
337     else
338     {
339         audio_music_fade_to(2.0f, level->song);
340         return game_init(level, lg->time, lg->goal);
341     }
342 }
343
344 void demo_play_step(float dt)
345 {
346     static float fps_track = 0.0f;
347     static float fps_cap   = 1.0f / (float) DEMO_FPS_CAP;
348
349     if (demo_fp)
350     {
351         fps_track += dt;
352         if (fps_track > fps_cap)
353         {            
354             put_float(demo_fp, &fps_track);
355             put_game_state(demo_fp); 
356             fps_track = 0.0f;
357         }
358     }
359 }
360
361 void demo_play_stop(const struct level_game *lg)
362 /* Update the demo header using the final level state. */
363 {
364     if (demo_fp)
365     {
366         demo_header_stop(demo_fp, lg->coins, lg->timer, lg->state);
367         fclose(demo_fp);
368         demo_fp = NULL;
369     }
370 }
371
372 int demo_play_saved(void)
373 {
374     return demo_exists(USER_REPLAY_FILE);
375 }
376
377 void demo_play_save(const char *name)
378 {
379     char src[PATHMAX];
380     char dst[PATHMAX];
381
382     if (name && demo_exists(USER_REPLAY_FILE)
383         && strcmp(name, USER_REPLAY_FILE) != 0)
384     {
385         strncpy(src, config_user(USER_REPLAY_FILE), PATHMAX);
386         strcat(src, REPLAY_EXT);
387         strncpy(dst, config_user(name), PATHMAX);
388         strcat(dst, REPLAY_EXT);
389
390         rename(src, dst);
391     }
392 }
393
394 /*---------------------------------------------------------------------------*/
395
396 static int demo_load_level(const struct demo *demo, struct level *level)
397 /* Load the level of the demo and fill the level structure */
398 {
399     if (level_load(demo->file, level))
400     {
401         level->time = demo->time;
402         level->goal = demo->goal;
403         return 1;
404     }
405     else
406         return 0;
407 }
408
409 static struct demo  demo_replay;       /* The current demo */
410 static struct level demo_level_replay; /* The current level demo-ed*/
411
412 const struct demo *curr_demo_replay(void)
413 {
414     return &demo_replay;
415 }
416
417
418 int demo_replay_init(const char *name, struct level_game *lg)
419 /* Internally load a replay an fill the lg structure (if not NULL) */
420 {
421     if ((demo_fp = demo_header_read(name, &demo_replay)))
422     {
423         if (!demo_load_level(&demo_replay, &demo_level_replay))
424             return 0;
425
426         if (lg)
427         {
428             lg->mode = demo_replay.mode;
429             lg->score = demo_replay.score;
430             lg->times = demo_replay.times;
431             lg->time = demo_replay.time;
432             lg->goal = demo_replay.goal;
433
434             /* A normal replay demo */
435             audio_music_fade_to(0.5f, demo_replay.song);
436             return game_init(&demo_level_replay, demo_replay.time,
437                              demo_replay.goal);
438         }
439         else                    /* A title screen demo */
440             return game_init(&demo_level_replay, demo_replay.time, 0);
441     }
442
443     return 0;
444 }
445
446 int demo_replay_step(float *dt)
447 {
448     const float g[3] = { 0.0f, -9.8f, 0.0f };
449     int sv;
450
451     if (demo_fp)
452     {
453         get_float(demo_fp, dt);
454
455         if (feof(demo_fp) == 0)
456         {
457             /* Play out current game state for particles, clock, etc. */
458
459             game_step(g, *dt, &sv);
460
461             /* Load real current game state from file. */
462             
463             if (get_game_state(demo_fp))
464                 return 1;
465         }
466     }
467     return 0;
468 }
469
470 void demo_replay_stop(int d)
471 {
472     if (demo_fp)
473     {
474         fclose(demo_fp);
475         demo_fp = NULL;
476
477         if (d) unlink(demo_replay.filename);
478     }
479 }
480
481 void demo_replay_dump_info(void)
482 {
483     demo_dump_info(&demo_replay);
484 }
485
486 /*---------------------------------------------------------------------------*/