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.
27 /*---------------------------------------------------------------------------*/
29 #define MAGIC 0x52424EAF
30 #define DEMO_VERSION 3
36 static struct demo demos[MAXDEMO]; /* Array of scanned demos */
37 static int count; /* Number of scanned demos */
39 /*---------------------------------------------------------------------------*/
41 void demo_dump_info(const struct demo *d)
62 d->timer, d->coins, d->mode, d->status, ctime(&d->date),
64 d->shot, d->file, d->back, d->grad, d->song,
65 d->time, d->goal, d->score, d->balls, d->times);
68 static time_t make_time_from_utc(struct tm *tm)
70 struct tm local, *utc;
75 local = *localtime(&t);
78 local.tm_year += local.tm_year - utc->tm_year;
79 local.tm_mon += local.tm_mon - utc->tm_mon ;
80 local.tm_mday += local.tm_mday - utc->tm_mday;
81 local.tm_hour += local.tm_hour - utc->tm_hour;
82 local.tm_min += local.tm_min - utc->tm_min ;
83 local.tm_sec += local.tm_sec - utc->tm_sec ;
85 return mktime(&local);
88 static int demo_header_read(FILE *fp, struct demo *d)
95 char datestr[DATELEN];
97 get_index(fp, &magic);
98 get_index(fp, &version);
102 if (magic == MAGIC && version == DEMO_VERSION && t)
106 get_index(fp, &d->coins);
107 get_index(fp, &d->status);
108 get_index(fp, &d->mode);
110 fread(d->player, 1, MAXNAM, fp);
112 fread(datestr, 1, DATELEN, fp);
122 /* Convert certain values to valid structure member values. */
124 date.tm_year -= 1900;
127 d->date = make_time_from_utc(&date);
129 fread(d->shot, 1, PATHMAX, fp);
130 fread(d->file, 1, PATHMAX, fp);
132 get_index(fp, &d->time);
133 get_index(fp, &d->goal);
134 get_index(fp, &d->score);
135 get_index(fp, &d->balls);
136 get_index(fp, &d->times);
143 static char *bname(const char *name, const char *suffix)
145 static char buf[MAXSTR];
150 /* Remove the directory delimiter */
152 base = strrchr(name, '/');
155 base = strrchr(name, '\\');
159 if ((tmp = strrchr(base, '\\')))
163 strncpy(buf, base ? base + 1 : name, MAXSTR);
165 /* Remove the extension */
167 l = strlen(buf) - strlen(suffix);
168 if ((l > 1) && (strcmp(buf + l, suffix) == 0))
174 static void demo_header_write(FILE *fp, struct demo *d)
177 int version = DEMO_VERSION;
180 char datestr[DATELEN];
182 strftime(datestr, DATELEN, "%Y-%m-%dT%H:%M:%S", gmtime(&d->date));
184 put_index(fp, &magic);
185 put_index(fp, &version);
186 put_index(fp, &zero);
187 put_index(fp, &zero);
188 put_index(fp, &zero);
189 put_index(fp, &d->mode);
191 fwrite(d->player, 1, MAXNAM, fp);
192 fwrite(datestr, 1, DATELEN, fp);
194 fwrite(d->shot, 1, PATHMAX, fp);
195 fwrite(d->file, 1, PATHMAX, fp);
197 put_index(fp, &d->time);
198 put_index(fp, &d->goal);
199 put_index(fp, &d->score);
200 put_index(fp, &d->balls);
201 put_index(fp, &d->times);
204 /*---------------------------------------------------------------------------*/
206 /* Scan another file (used by demo_scan). */
208 static void demo_scan_file(const char *filename)
211 struct demo *d = &demos[count];
213 if ((fp = fopen(config_user(filename), FMODE_RB)))
215 if (demo_header_read(fp, d))
217 strncpy(d->filename, config_user(filename), MAXSTR);
218 strncpy(d->name, bname(filename, REPLAY_EXT), PATHMAX);
219 d->name[PATHMAX - 1] = '\0';
236 /* Scan the user directory for files. */
238 if ((h = FindFirstFile(config_user("*"), &d)) != INVALID_HANDLE_VALUE)
241 demo_scan_file(d.cFileName);
242 while (count < MAXDEMO && FindNextFile(h, &d));
259 /* Scan the user directory for files. */
261 if ((dp = opendir(config_user(""))))
263 while (count < MAXDEMO && (ent = readdir(dp)))
264 demo_scan_file(ent->d_name);
272 const char *demo_pick(void)
276 return (n > 0) ? demos[(rand() >> 4) % n].filename : NULL;
279 const struct demo *demo_get(int i)
281 return (0 <= i && i < count) ? &demos[i] : NULL;
284 const char *date_to_str(time_t i)
286 static char str[MAXSTR];
288 /* TRANSLATORS: here is the format of the date shown at the
289 replay selection screen. The default will work in most cases, so
290 you should only change it if something's horribly wrong, like,
291 for instance, the GUI layout is broken. See strftime(3) for
292 details on the format.
295 strftime(str, MAXSTR, /* xgettext:no-c-format */ _("%c"), localtime(&i));
300 /*---------------------------------------------------------------------------*/
302 int demo_exists(const char *name)
307 strcpy(buf, config_user(name));
308 strcat(buf, REPLAY_EXT);
309 if ((fp = fopen(buf, "r")))
317 void demo_unique(char *name)
321 /* Generate a unique name for a new replay save. */
323 for (i = 1; i < 100; i++)
325 sprintf(name, "replay%02d", i);
327 if (!demo_exists(name))
332 /*---------------------------------------------------------------------------*/
334 int demo_play_init(const char *name,
335 const struct level *level,
336 const struct level_game *lg)
340 memset(&demo, 0, sizeof (demo));
342 strncpy(demo.filename, config_user(name), MAXSTR);
343 strcat(demo.filename, REPLAY_EXT);
345 demo.mode = lg->mode;
346 demo.date = time(NULL);
348 config_get_s(CONFIG_PLAYER, demo.player, MAXNAM);
350 strncpy(demo.shot, level->shot, PATHMAX);
351 strncpy(demo.file, level->file, PATHMAX);
352 strncpy(demo.back, level->back, PATHMAX);
353 strncpy(demo.grad, level->grad, PATHMAX);
354 strncpy(demo.song, level->song, PATHMAX);
356 demo.time = lg->time;
357 demo.goal = lg->goal;
358 demo.score = lg->score;
359 demo.balls = lg->balls;
360 demo.times = lg->times;
362 if ((demo_fp = fopen(demo.filename, FMODE_WB)))
364 demo_header_write(demo_fp, &demo);
365 audio_music_fade_to(2.0f, level->song);
366 return game_init(level, lg->time, lg->goal);
371 void demo_play_step(float dt)
375 put_float(demo_fp, &dt);
376 put_game_state(demo_fp);
380 void demo_play_stat(const struct level_game *lg)
384 long pos = ftell(demo_fp);
386 fseek(demo_fp, 8, SEEK_SET);
388 put_index(demo_fp, &lg->timer);
389 put_index(demo_fp, &lg->coins);
390 put_index(demo_fp, &lg->status);
392 fseek(demo_fp, pos, SEEK_SET);
396 void demo_play_stop(void)
407 return demo_exists(USER_REPLAY_FILE);
410 void demo_rename(const char *name)
416 demo_exists(USER_REPLAY_FILE) &&
417 strcmp(name, USER_REPLAY_FILE) != 0)
419 strcpy(src, config_user(USER_REPLAY_FILE));
420 strcat(src, REPLAY_EXT);
422 strcpy(dst, config_user(name));
423 strcat(dst, REPLAY_EXT);
429 /*---------------------------------------------------------------------------*/
431 static int demo_load_level(const struct demo *demo, struct level *level)
433 if (level_load(demo->file, level))
435 level->time = demo->time;
436 level->goal = demo->goal;
442 static struct demo demo_replay; /* The current demo */
443 static struct level demo_level_replay; /* The current level demo-ed*/
445 const struct demo *curr_demo_replay(void)
450 static int demo_status = GAME_NONE;
452 int demo_replay_init(const char *name, struct level_game *lg)
454 demo_status = GAME_NONE;
455 demo_fp = fopen(name, FMODE_RB);
457 if (demo_fp && demo_header_read(demo_fp, &demo_replay))
459 strncpy(demo_replay.filename, name, MAXSTR);
460 strncpy(demo_replay.name, bname(name, REPLAY_EXT), PATHMAX);
462 if (!demo_load_level(&demo_replay, &demo_level_replay))
467 lg->mode = demo_replay.mode;
468 lg->score = demo_replay.score;
469 lg->times = demo_replay.times;
470 lg->time = demo_replay.time;
471 lg->goal = demo_replay.goal;
473 /* A normal replay demo */
474 audio_music_fade_to(0.5f, demo_level_replay.song);
475 return game_init(&demo_level_replay, demo_replay.time,
478 else /* A title screen demo */
479 return game_init(&demo_level_replay, demo_replay.time, 0);
484 int demo_replay_step(float *dt)
486 const float g[3] = { 0.0f, -9.8f, 0.0f };
490 get_float(demo_fp, dt);
492 if (feof(demo_fp) == 0)
494 /* Play out current game state for particles, clock, etc. */
496 if (demo_status == GAME_NONE)
497 demo_status = game_step(g, *dt, 1);
499 game_step(g, *dt, 0);
501 /* Load real current game state from file. */
503 if (get_game_state(demo_fp))
510 void demo_replay_stop(int d)
517 if (d) remove(demo_replay.filename);
521 void demo_replay_dump_info(void)
523 demo_dump_info(&demo_replay);
526 /*---------------------------------------------------------------------------*/