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.
28 /*---------------------------------------------------------------------------*/
30 #define MAGIC 0x52424EAF
31 #define DEMO_VERSION 4
37 static struct demo demos[MAXDEMO]; /* Array of scanned demos */
38 static int count; /* Number of scanned demos */
40 /*---------------------------------------------------------------------------*/
42 void demo_dump_info(const struct demo *d)
63 d->timer, d->coins, d->mode, d->status, ctime(&d->date),
65 d->shot, d->file, d->back, d->grad, d->song,
66 d->time, d->goal, d->score, d->balls, d->times);
69 static time_t make_time_from_utc(struct tm *tm)
71 struct tm local, *utc;
76 local = *localtime(&t);
79 local.tm_year += local.tm_year - utc->tm_year;
80 local.tm_mon += local.tm_mon - utc->tm_mon ;
81 local.tm_mday += local.tm_mday - utc->tm_mday;
82 local.tm_hour += local.tm_hour - utc->tm_hour;
83 local.tm_min += local.tm_min - utc->tm_min ;
84 local.tm_sec += local.tm_sec - utc->tm_sec ;
86 return mktime(&local);
89 static int demo_header_read(FILE *fp, struct demo *d)
96 char datestr[DATELEN];
98 get_index(fp, &magic);
99 get_index(fp, &version);
103 if (magic == MAGIC && version == DEMO_VERSION && t)
107 get_index(fp, &d->coins);
108 get_index(fp, &d->status);
109 get_index(fp, &d->mode);
111 get_string(fp, d->player, MAXNAM);
113 get_string(fp, datestr, DATELEN);
123 /* Convert certain values to valid structure member values. */
125 date.tm_year -= 1900;
128 d->date = make_time_from_utc(&date);
130 get_string(fp, d->shot, PATHMAX);
131 get_string(fp, d->file, PATHMAX);
133 get_index(fp, &d->time);
134 get_index(fp, &d->goal);
135 get_index(fp, &d->score);
136 get_index(fp, &d->balls);
137 get_index(fp, &d->times);
144 static char *bname(const char *name, const char *suffix)
146 static char buf[MAXSTR];
151 /* Remove the directory delimiter */
153 base = strrchr(name, '/');
156 base = strrchr(name, '\\');
160 if ((tmp = strrchr(base, '\\')))
164 strncpy(buf, base ? base + 1 : name, MAXSTR);
166 /* Remove the extension */
168 l = strlen(buf) - strlen(suffix);
169 if ((l > 1) && (strcmp(buf + l, suffix) == 0))
175 static void demo_header_write(FILE *fp, struct demo *d)
178 int version = DEMO_VERSION;
181 char datestr[DATELEN];
183 strftime(datestr, DATELEN, "%Y-%m-%dT%H:%M:%S", gmtime(&d->date));
185 put_index(fp, &magic);
186 put_index(fp, &version);
187 put_index(fp, &zero);
188 put_index(fp, &zero);
189 put_index(fp, &zero);
190 put_index(fp, &d->mode);
192 put_string(fp, d->player);
193 put_string(fp, datestr);
195 put_string(fp, d->shot);
196 put_string(fp, d->file);
198 put_index(fp, &d->time);
199 put_index(fp, &d->goal);
200 put_index(fp, &d->score);
201 put_index(fp, &d->balls);
202 put_index(fp, &d->times);
205 /*---------------------------------------------------------------------------*/
207 /* Scan another file (used by demo_scan). */
209 static void demo_scan_file(const char *filename)
212 struct demo *d = &demos[count];
214 if ((fp = fopen(config_user(filename), FMODE_RB)))
216 if (demo_header_read(fp, d))
218 strncpy(d->filename, config_user(filename), MAXSTR);
219 strncpy(d->name, bname(text_from_locale(d->filename), REPLAY_EXT),
221 d->name[PATHMAX - 1] = '\0';
238 /* Scan the user directory for files. */
240 if ((h = FindFirstFile(config_user("*"), &d)) != INVALID_HANDLE_VALUE)
243 demo_scan_file(d.cFileName);
244 while (count < MAXDEMO && FindNextFile(h, &d));
261 /* Scan the user directory for files. */
263 if ((dp = opendir(config_user(""))))
265 while (count < MAXDEMO && (ent = readdir(dp)))
266 demo_scan_file(ent->d_name);
274 const char *demo_pick(void)
278 return (n > 0) ? demos[(rand() >> 4) % n].filename : NULL;
281 const struct demo *demo_get(int i)
283 return (0 <= i && i < count) ? &demos[i] : NULL;
286 const char *date_to_str(time_t i)
288 static char str[MAXSTR];
291 /* TRANSLATORS: here is the format of the date shown at the
292 replay selection screen (and possibly elsewhere). The default
293 format is necessarily locale-independent. See strftime(3) for
294 details on the format.
297 fmt = /* xgettext:no-c-format */ L_("%Y-%m-%d %H:%M:%S");
298 strftime(str, MAXSTR, fmt, localtime(&i));
299 return text_from_locale(str);
302 /*---------------------------------------------------------------------------*/
304 int demo_exists(const char *name)
309 strcpy(buf, config_user(name));
310 strcat(buf, REPLAY_EXT);
311 if ((fp = fopen(buf, "r")))
319 void demo_unique(char *name)
323 /* Generate a unique name for a new replay save. */
325 for (i = 1; i < 100; i++)
327 sprintf(name, "replay%02d", i);
329 if (!demo_exists(name))
334 /*---------------------------------------------------------------------------*/
336 int demo_play_init(const char *name,
337 const struct level *level,
338 const struct level_game *lg)
342 memset(&demo, 0, sizeof (demo));
344 strncpy(demo.filename, config_user(name), MAXSTR);
345 strcat(demo.filename, REPLAY_EXT);
347 demo.mode = lg->mode;
348 demo.date = time(NULL);
350 config_get_s(CONFIG_PLAYER, demo.player, MAXNAM);
352 strncpy(demo.shot, level->shot, PATHMAX);
353 strncpy(demo.file, level->file, PATHMAX);
354 strncpy(demo.back, level->back, PATHMAX);
355 strncpy(demo.grad, level->grad, PATHMAX);
356 strncpy(demo.song, level->song, PATHMAX);
358 demo.time = lg->time;
359 demo.goal = lg->goal;
360 demo.score = lg->score;
361 demo.balls = lg->balls;
362 demo.times = lg->times;
364 if ((demo_fp = fopen(demo.filename, FMODE_WB)))
366 demo_header_write(demo_fp, &demo);
367 audio_music_fade_to(2.0f, level->song);
368 return game_init(level, lg->time, lg->goal);
373 void demo_play_step(float dt)
377 put_float(demo_fp, &dt);
378 put_game_state(demo_fp);
382 void demo_play_stat(const struct level_game *lg)
386 long pos = ftell(demo_fp);
388 fseek(demo_fp, 8, SEEK_SET);
390 put_index(demo_fp, &lg->timer);
391 put_index(demo_fp, &lg->coins);
392 put_index(demo_fp, &lg->status);
394 fseek(demo_fp, pos, SEEK_SET);
398 void demo_play_stop(void)
409 return demo_exists(USER_REPLAY_FILE);
412 void demo_rename(const char *name)
418 demo_exists(USER_REPLAY_FILE) &&
419 strcmp(name, USER_REPLAY_FILE) != 0)
421 strcpy(src, config_user(USER_REPLAY_FILE));
422 strcat(src, REPLAY_EXT);
424 strcpy(dst, config_user(name));
425 strcat(dst, REPLAY_EXT);
428 if (demo_exists(name))
435 /*---------------------------------------------------------------------------*/
437 static int demo_load_level(const struct demo *demo, struct level *level)
439 if (level_load(demo->file, level))
441 level->time = demo->time;
442 level->goal = demo->goal;
448 static struct demo demo_replay; /* The current demo */
449 static struct level demo_level_replay; /* The current level demo-ed*/
451 const struct demo *curr_demo_replay(void)
456 static int demo_status = GAME_NONE;
458 int demo_replay_init(const char *name, struct level_game *lg)
460 demo_status = GAME_NONE;
461 demo_fp = fopen(name, FMODE_RB);
463 if (demo_fp && demo_header_read(demo_fp, &demo_replay))
465 strncpy(demo_replay.filename, name, MAXSTR);
466 strncpy(demo_replay.name, bname(text_from_locale(demo_replay.filename),
467 REPLAY_EXT), PATHMAX);
469 if (!demo_load_level(&demo_replay, &demo_level_replay))
474 lg->mode = demo_replay.mode;
475 lg->score = demo_replay.score;
476 lg->times = demo_replay.times;
477 lg->time = demo_replay.time;
478 lg->goal = demo_replay.goal;
480 /* A normal replay demo */
481 audio_music_fade_to(0.5f, demo_level_replay.song);
482 return game_init(&demo_level_replay, demo_replay.time,
485 else /* A title screen demo */
486 return game_init(&demo_level_replay, demo_replay.time, 0);
491 int demo_replay_step(float *dt)
493 const float g[3] = { 0.0f, -9.8f, 0.0f };
497 get_float(demo_fp, dt);
499 if (feof(demo_fp) == 0)
501 /* Play out current game state for particles, clock, etc. */
503 if (demo_status == GAME_NONE)
504 demo_status = game_step(g, *dt, 1);
506 game_step(g, *dt, 0);
508 /* Load real current game state from file. */
510 if (get_game_state(demo_fp))
517 void demo_replay_stop(int d)
524 if (d) remove(demo_replay.filename);
528 void demo_replay_dump_info(void)
530 demo_dump_info(&demo_replay);
533 /*---------------------------------------------------------------------------*/