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.
33 /*---------------------------------------------------------------------------*/
35 #define MAGIC 0x4E425252
36 #define DEMO_FPS_CAP 200 /* FPS replay limit, keeps size down on monster systems */
41 /* Demo information structure (header) */
44 char name[MAXNAM]; /* demo filename */
47 int timer; /* elapsed time */
48 int coins; /* coin number */
49 int state; /* how the replay end */
50 int mode; /* game mode */
51 time_t date; /* date of creation */
52 char player[MAXNAM]; /* player name */
53 char shot[PATHMAX]; /* image filename */
54 char file[PATHMAX]; /* level filename */
55 char back[PATHMAX]; /* level bg filename */
56 char grad[PATHMAX]; /* level gradiant filename */
57 char song[PATHMAX]; /* level song filename */
58 int time; /* time limit (! training mode) */
59 int goal; /* coin to open the goal (! training mode) */
60 int score; /* sum of coins (challenge mode) */
61 int balls; /* number of balls (challenge mode) */
62 int total_time; /* total time (challenge mode) */
66 static struct demo demos[MAXDEMO]; /* Array of scanned demos */
68 static int count; /* number of scanned demos */
70 /*---------------------------------------------------------------------------*/
72 void demo_dump_info(struct demo * d)
73 /* This function dump the info of a demo structure*/
75 printf("Filename: %s\n"
93 d->timer, d->coins, d->mode, d->state, ctime(&d->date),
95 d->shot, d->file, d->back, d->grad, d->song,
96 d->time, d->goal, d->score, d->balls, d->total_time);
99 static FILE * demo_header(const char * filename, struct demo * d)
100 /* Open a demo file, fill the demo information structure
101 If success, return the file pointer positioned after the header
102 If fail, return null */
105 if ((fp = fopen(config_user(filename), FMODE_RB)))
110 get_index(fp, &magic);
113 if (magic == MAGIC && t)
115 strncpy(d->name, filename, MAXNAM);
117 get_index(fp, &d->coins);
118 get_index(fp, &d->state);
119 get_index(fp, &d->mode);
120 get_index(fp, (int*)&d->date);
121 fread(d->player, 1, MAXNAM, fp);
122 fread(d->shot, 1, PATHMAX, fp);
123 fread(d->file, 1, PATHMAX, fp);
124 fread(d->back, 1, PATHMAX, fp);
125 fread(d->grad, 1, PATHMAX, fp);
126 fread(d->song, 1, PATHMAX, fp);
127 get_index(fp, &d->time);
128 get_index(fp, &d->goal);
129 get_index(fp, &d->score);
130 get_index(fp, &d->balls);
131 get_index(fp, &d->total_time);
140 /*---------------------------------------------------------------------------*/
142 static void demo_scan_file(const char * filename)
143 /* Scan a other file (used by demo_scan */
146 if ((fp = demo_header(filename, &demos[count])))
162 /* Scan the user directory for files. */
164 if ((h = FindFirstFile(config_user("*"), &d)) != INVALID_HANDLE_VALUE)
167 demo_scan_file(d.cFileName);
168 while (count < MAXDEMO && FindNextFile(h, &d));
185 /* Scan the user directory for files. */
187 if ((dp = opendir(config_user(""))))
189 while (count < MAXDEMO && (ent = readdir(dp)))
190 demo_scan_file(ent->d_name);
198 const char *demo_pick(void)
202 return (n > 0) ? demos[(rand() >> 4) % n].name : NULL;
205 const char *demo_name(int i)
207 return (0 <= i && i < count) ? demos[i].name : NULL;
210 const char *demo_shot(int i)
212 return (0 <= i && i < count) ? demos[i].shot : NULL;
215 int demo_coins(int i)
217 return (0 <= i && i < count) ? demos[i].coins : 0;
220 int demo_state(int i)
222 return (0 <= i && i < count) ? demos[i].state : 0;
227 return (0 <= i && i < count) ? demos[i].mode : 0;
230 int demo_clock(int i)
232 return (0 <= i && i < count) ? demos[i].timer : 0;
235 time_t demo_date(int i)
237 return (0 <= i && i < count) ? demos[i].date : 0;
240 void demo_str_date(int i, char * str, int len)
242 time_t d = demo_date(i);
243 struct tm * tm = localtime(&d);
244 strftime (str, len, "%c",tm);
247 const char * demo_player(int i)
249 return (0 <= i && i < count) ? demos[i].player : "";
252 /*---------------------------------------------------------------------------*/
254 int demo_exists(char *name)
258 if ((fp = fopen(config_user(name), "r")))
266 void demo_unique(char *name)
270 /* Generate a unique name for a new replay save. */
272 for (i = 1; i < 100; i++)
274 sprintf(name, _("replay%02d"), i);
276 if (!demo_exists(name))
281 /*---------------------------------------------------------------------------*/
283 int demo_play_init(const char *name,
289 int mode, int tim, int goal, int score, int balls, int total_time)
293 int date = time(NULL);
296 config_get_s(CONFIG_PLAYER, player, MAXNAM);
298 /* Initialize the replay file. Write the header. */
300 if (name && (demo_fp = fopen(config_user(name), FMODE_WB)))
302 put_index(demo_fp, &magic);
303 put_index(demo_fp, &zero);
304 put_index(demo_fp, &zero);
305 put_index(demo_fp, &zero);
306 put_index(demo_fp, &mode);
307 put_index(demo_fp, &date);
309 fwrite(player, 1, MAXNAM, demo_fp);
311 fwrite(shot, 1, PATHMAX, demo_fp);
312 fwrite(file, 1, PATHMAX, demo_fp);
313 fwrite(back, 1, PATHMAX, demo_fp);
314 fwrite(grad, 1, PATHMAX, demo_fp);
315 fwrite(song, 1, PATHMAX, demo_fp);
317 put_index(demo_fp, &tim);
318 put_index(demo_fp, &goal);
319 put_index(demo_fp, &score);
320 put_index(demo_fp, &balls);
321 put_index(demo_fp, &total_time);
323 audio_music_fade_to(2.0f, song);
325 return game_init(file, back, grad, tim, (goal == 0));
330 void demo_play_step(float dt)
332 static float fps_track = 0.0f;
333 static float fps_cap = 1.0f / (float) DEMO_FPS_CAP;
338 if (fps_track > fps_cap)
340 put_float(demo_fp, &fps_track);
341 put_game_state(demo_fp);
347 void demo_play_stop(int coins, int timer, int state)
351 /* Update the demo header using the final coins and time. */
353 fseek(demo_fp, 4, SEEK_SET);
355 put_index(demo_fp, &timer);
356 put_index(demo_fp, &coins);
357 put_index(demo_fp, &state);
364 int demo_play_saved(void)
366 return demo_exists(USER_REPLAY_FILE);
369 void demo_play_save(const char *name)
374 if (name && demo_exists(USER_REPLAY_FILE) && strcmp(name, USER_REPLAY_FILE) != 0)
376 strncpy(src, config_user(USER_REPLAY_FILE), PATHMAX);
377 strncpy(dst, config_user(name), PATHMAX);
383 /*---------------------------------------------------------------------------*/
385 static char demo_replay_name[MAXSTR];
387 int demo_replay_init(const char *name, int *m, int *s, int *c, int *g, int *t)
391 if ((demo_fp = demo_header(name, &d)))
393 strncpy(demo_replay_name, name, MAXSTR);
397 if (t) *t = d.total_time;
401 audio_music_fade_to(0.5f, d.song);
402 return game_init(d.file, d.back, d.grad, d.time, (d.goal == 0));
405 return game_init(d.file, d.back, d.grad, d.time, 1);
411 int demo_replay_step(float *dt)
413 const float g[3] = { 0.0f, -9.8f, 0.0f };
417 get_float(demo_fp, dt);
419 if (feof(demo_fp) == 0)
421 /* Play out current game state for particles, clock, etc. */
423 game_step(g, *dt, 1);
425 /* Load real current game state from file. */
427 if (get_game_state(demo_fp))
434 void demo_replay_stop(int d)
441 if (d) unlink(config_user(demo_replay_name));
445 /*---------------------------------------------------------------------------*/