2 * Copyright (C) 2003 Robert Kooima
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.
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.
32 /*---------------------------------------------------------------------------*/
34 #define MAGIC 0x4E425252
35 #define DEMO_FPS_CAP 200 /* FPS replay limit, keeps size down on monster systems */
40 /* Demo information structure (header) */
43 char name[MAXNAM]; /* demo filename */
46 int timer; /* elapsed time */
47 int coins; /* coin number */
48 int state; /* how the replay end */
49 int mode; /* game mode */
50 time_t date; /* date of creation */
51 char player[MAXNAM]; /* player name */
52 char shot[PATHMAX]; /* image filename */
53 char file[PATHMAX]; /* level filename */
54 char back[PATHMAX]; /* level bg filename */
55 char grad[PATHMAX]; /* level gradiant filename */
56 char song[PATHMAX]; /* level song filename */
57 int time; /* time limit (! training mode) */
58 int goal; /* coin to open the goal (! training mode) */
59 int score; /* sum of coins (challenge mode) */
60 int balls; /* number of balls (challenge mode) */
61 int total_time; /* total time (challenge mode) */
65 static struct demo demos[MAXDEMO]; /* Array of scanned demos */
67 static int count; /* number of scanned demos */
69 /*---------------------------------------------------------------------------*/
71 void demo_dump_info(struct demo * d)
72 /* This function dump the info of a demo structure*/
74 printf("Filename: %s\n"
92 d->timer, d->coins, d->mode, d->state, ctime(&d->date),
94 d->shot, d->file, d->back, d->grad, d->song,
95 d->time, d->goal, d->score, d->balls, d->total_time);
98 static FILE * demo_header(const char * filename, struct demo * d)
99 /* Open a demo file, fill the demo information structure
100 If success, return the file pointer positioned after the header
101 If fail, return null */
104 if ((fp = fopen(config_user(filename), FMODE_RB)))
109 get_index(fp, &magic);
112 if (magic == MAGIC && t)
114 strncpy(d->name, filename, MAXNAM);
116 get_index(fp, &d->coins);
117 get_index(fp, &d->state);
118 get_index(fp, &d->mode);
119 get_index(fp, (int*)&d->date);
120 fread(d->player, 1, MAXNAM, fp);
121 fread(d->shot, 1, PATHMAX, fp);
122 fread(d->file, 1, PATHMAX, fp);
123 fread(d->back, 1, PATHMAX, fp);
124 fread(d->grad, 1, PATHMAX, fp);
125 fread(d->song, 1, PATHMAX, fp);
126 get_index(fp, &d->time);
127 get_index(fp, &d->goal);
128 get_index(fp, &d->score);
129 get_index(fp, &d->balls);
130 get_index(fp, &d->total_time);
139 /*---------------------------------------------------------------------------*/
141 static void demo_scan_file(const char * filename)
142 /* Scan a other file (used by demo_scan */
145 if ((fp = demo_header(filename, &demos[count])))
161 /* Scan the user directory for files. */
163 if ((h = FindFirstFile(config_user("*"), &d)) != INVALID_HANDLE_VALUE)
166 demo_scan_file(d.cFileName);
167 while (count < MAXDEMO && FindNextFile(h, &d));
184 /* Scan the user directory for files. */
186 if ((dp = opendir(config_user(""))))
188 while (count < MAXDEMO && (ent = readdir(dp)))
189 demo_scan_file(ent->d_name);
197 const char *demo_pick(void)
201 return (n > 0) ? demos[(rand() >> 4) % n].name : NULL;
204 const char *demo_name(int i)
206 return (0 <= i && i < count) ? demos[i].name : NULL;
209 const char *demo_shot(int i)
211 return (0 <= i && i < count) ? demos[i].shot : NULL;
214 int demo_coins(int i)
216 return (0 <= i && i < count) ? demos[i].coins : 0;
219 int demo_state(int i)
221 return (0 <= i && i < count) ? demos[i].state : 0;
226 return (0 <= i && i < count) ? demos[i].mode : 0;
229 int demo_clock(int i)
231 return (0 <= i && i < count) ? demos[i].timer : 0;
234 time_t demo_date(int i)
236 return (0 <= i && i < count) ? demos[i].date : 0;
239 void demo_str_date(int i, char * str, int len)
241 time_t d = demo_date(i);
242 struct tm * tm = localtime(&d);
243 strftime (str, len, "%c",tm);
246 const char * demo_player(int i)
248 return (0 <= i && i < count) ? demos[i].player : "";
251 /*---------------------------------------------------------------------------*/
253 int demo_exists(char *name)
257 if ((fp = fopen(config_user(name), "r")))
265 void demo_unique(char *name)
269 /* Generate a unique name for a new replay save. */
271 for (i = 1; i < 100; i++)
273 sprintf(name, _("replay%02d"), i);
275 if (!demo_exists(name))
280 /*---------------------------------------------------------------------------*/
282 int demo_play_init(const char *name,
288 int mode, int tim, int goal, int score, int balls, int total_time)
292 int date = time(NULL);
295 config_get_s(CONFIG_PLAYER, player, MAXNAM);
297 /* Initialize the replay file. Write the header. */
299 if (name && (demo_fp = fopen(config_user(name), FMODE_WB)))
301 put_index(demo_fp, &magic);
302 put_index(demo_fp, &zero);
303 put_index(demo_fp, &zero);
304 put_index(demo_fp, &zero);
305 put_index(demo_fp, &mode);
306 put_index(demo_fp, &date);
308 fwrite(player, 1, MAXNAM, demo_fp);
310 fwrite(shot, 1, PATHMAX, demo_fp);
311 fwrite(file, 1, PATHMAX, demo_fp);
312 fwrite(back, 1, PATHMAX, demo_fp);
313 fwrite(grad, 1, PATHMAX, demo_fp);
314 fwrite(song, 1, PATHMAX, demo_fp);
316 put_index(demo_fp, &tim);
317 put_index(demo_fp, &goal);
318 put_index(demo_fp, &score);
319 put_index(demo_fp, &balls);
320 put_index(demo_fp, &total_time);
322 audio_music_fade_to(2.0f, song);
324 return game_init(file, back, grad, tim, goal);
329 void demo_play_step(float dt)
331 static float fps_track = 0.0f;
332 static float fps_cap = 1.0f / (float) DEMO_FPS_CAP;
337 if (fps_track > fps_cap)
339 put_float(demo_fp, &fps_track);
340 put_game_state(demo_fp);
346 void demo_play_stop(int coins, int timer, int state)
350 /* Update the demo header using the final coins and time. */
352 fseek(demo_fp, 4, SEEK_SET);
354 put_index(demo_fp, &timer);
355 put_index(demo_fp, &coins);
356 put_index(demo_fp, &state);
363 int demo_play_saved(void)
365 return demo_exists(USER_REPLAY_FILE);
368 void demo_play_save(const char *name)
373 if (name && demo_exists(USER_REPLAY_FILE) && strcmp(name, USER_REPLAY_FILE) != 0)
375 strncpy(src, config_user(USER_REPLAY_FILE), PATHMAX);
376 strncpy(dst, config_user(name), PATHMAX);
382 /*---------------------------------------------------------------------------*/
384 static char demo_replay_name[MAXSTR];
386 int demo_replay_init(const char *name, int *m, int *s, int *c, int *t)
390 if ((demo_fp = demo_header(name, &d)))
392 strncpy(demo_replay_name, name, MAXSTR);
395 if (t) *t = d.total_time;
399 /* A normal replay demo */
400 audio_music_fade_to(0.5f, d.song);
401 return game_init(d.file, d.back, d.grad, d.time, d.goal);
403 else /* A title screen demo */
404 return game_init(d.file, d.back, d.grad, d.time, 0);
410 int demo_replay_step(float *dt)
412 const float g[3] = { 0.0f, -9.8f, 0.0f };
416 get_float(demo_fp, dt);
418 if (feof(demo_fp) == 0)
420 /* Play out current game state for particles, clock, etc. */
422 game_step(g, *dt, 1);
424 /* Load real current game state from file. */
426 if (get_game_state(demo_fp))
433 void demo_replay_stop(int d)
440 if (d) unlink(config_user(demo_replay_name));
444 /*---------------------------------------------------------------------------*/