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.
29 /*---------------------------------------------------------------------------*/
31 #define MAGIC 0x52424EAF
32 #define DEMO_VERSION 5
38 static struct demo demos[MAXDEMO]; /* Array of scanned demos */
39 static int count; /* Number of scanned demos */
41 /*---------------------------------------------------------------------------*/
43 void demo_dump_info(const struct demo *d)
64 d->timer, d->coins, d->mode, d->status, ctime(&d->date),
66 d->shot, d->file, d->back, d->grad, d->song,
67 d->time, d->goal, d->score, d->balls, d->times);
70 static int demo_header_read(FILE *fp, struct demo *d)
77 char datestr[DATELEN];
79 get_index(fp, &magic);
80 get_index(fp, &version);
84 if (magic == MAGIC && version == DEMO_VERSION && t)
88 get_index(fp, &d->coins);
89 get_index(fp, &d->status);
90 get_index(fp, &d->mode);
92 get_string(fp, d->player, MAXNAM);
93 get_string(fp, datestr, DATELEN);
104 date.tm_year -= 1900;
108 d->date = make_time_from_utc(&date);
110 get_string(fp, d->shot, PATHMAX);
111 get_string(fp, d->file, PATHMAX);
113 get_index(fp, &d->time);
114 get_index(fp, &d->goal);
115 get_index(fp, &d->score);
116 get_index(fp, &d->balls);
117 get_index(fp, &d->times);
124 static void demo_header_write(FILE *fp, struct demo *d)
127 int version = DEMO_VERSION;
130 char datestr[DATELEN];
132 strftime(datestr, DATELEN, "%Y-%m-%dT%H:%M:%S", gmtime(&d->date));
134 put_index(fp, &magic);
135 put_index(fp, &version);
136 put_index(fp, &zero);
137 put_index(fp, &zero);
138 put_index(fp, &zero);
139 put_index(fp, &d->mode);
141 put_string(fp, d->player);
142 put_string(fp, datestr);
144 put_string(fp, d->shot);
145 put_string(fp, d->file);
147 put_index(fp, &d->time);
148 put_index(fp, &d->goal);
149 put_index(fp, &d->score);
150 put_index(fp, &d->balls);
151 put_index(fp, &d->times);
154 /*---------------------------------------------------------------------------*/
156 /* Scan another file (used by demo_scan). */
158 static void demo_scan_file(const char *filename)
161 struct demo *d = &demos[count];
163 if ((fp = fopen(config_user(filename), FMODE_RB)))
165 if (demo_header_read(fp, d))
167 strncpy(d->filename, config_user(filename), MAXSTR);
169 base_name(text_from_locale(d->filename), REPLAY_EXT),
171 d->name[PATHMAX - 1] = '\0';
188 /* Scan the user directory for files. */
190 if ((h = FindFirstFile(config_user("*"), &d)) != INVALID_HANDLE_VALUE)
193 demo_scan_file(d.cFileName);
194 while (count < MAXDEMO && FindNextFile(h, &d));
211 /* Scan the user directory for files. */
213 if ((dp = opendir(config_user(""))))
215 while (count < MAXDEMO && (ent = readdir(dp)))
216 demo_scan_file(ent->d_name);
224 const char *demo_pick(void)
228 return (n > 0) ? demos[(rand() >> 4) % n].filename : NULL;
231 const struct demo *demo_get(int i)
233 return (0 <= i && i < count) ? &demos[i] : NULL;
236 /*---------------------------------------------------------------------------*/
238 int demo_exists(const char *name)
242 strcpy(buf, config_user(name));
243 strcat(buf, REPLAY_EXT);
245 return file_exists(buf);
248 void demo_unique(char *name)
252 /* Generate a unique name for a new replay save. */
254 for (i = 1; i < 100; i++)
256 sprintf(name, "replay%02d", i);
258 if (!demo_exists(name))
263 /*---------------------------------------------------------------------------*/
265 int demo_play_init(const char *name,
266 const struct level *level,
267 const struct level_game *lg)
271 memset(&demo, 0, sizeof (demo));
273 strncpy(demo.filename, config_user(name), MAXSTR);
274 strcat(demo.filename, REPLAY_EXT);
276 demo.mode = lg->mode;
277 demo.date = time(NULL);
279 config_get_s(CONFIG_PLAYER, demo.player, MAXNAM);
281 strncpy(demo.shot, level->shot, PATHMAX);
282 strncpy(demo.file, level->file, PATHMAX);
283 strncpy(demo.back, level->back, PATHMAX);
284 strncpy(demo.grad, level->grad, PATHMAX);
285 strncpy(demo.song, level->song, PATHMAX);
287 demo.time = lg->time;
288 demo.goal = lg->goal;
289 demo.score = lg->score;
290 demo.balls = lg->balls;
291 demo.times = lg->times;
293 if ((demo_fp = fopen(demo.filename, FMODE_WB)))
295 demo_header_write(demo_fp, &demo);
296 audio_music_fade_to(2.0f, level->song);
297 return game_init(level, lg->time, lg->goal);
302 void demo_play_step()
308 void demo_play_stat(const struct level_game *lg)
312 long pos = ftell(demo_fp);
314 fseek(demo_fp, 8, SEEK_SET);
316 put_index(demo_fp, &lg->timer);
317 put_index(demo_fp, &lg->coins);
318 put_index(demo_fp, &lg->status);
320 fseek(demo_fp, pos, SEEK_SET);
324 void demo_play_stop(void)
335 return demo_exists(USER_REPLAY_FILE);
338 void demo_rename(const char *name)
344 demo_exists(USER_REPLAY_FILE) &&
345 strcmp(name, USER_REPLAY_FILE) != 0)
347 strcpy(src, config_user(USER_REPLAY_FILE));
348 strcat(src, REPLAY_EXT);
350 strcpy(dst, config_user(name));
351 strcat(dst, REPLAY_EXT);
353 file_rename(src, dst);
357 /*---------------------------------------------------------------------------*/
359 static int demo_load_level(const struct demo *demo, struct level *level)
361 if (level_load(demo->file, level))
363 level->time = demo->time;
364 level->goal = demo->goal;
370 static struct demo demo_replay; /* The current demo */
371 static struct level demo_level_replay; /* The current level demo-ed*/
373 const struct demo *curr_demo_replay(void)
378 static int demo_status = GAME_NONE;
380 int demo_replay_init(const char *name, struct level_game *lg)
382 demo_status = GAME_NONE;
383 demo_fp = fopen(name, FMODE_RB);
385 if (demo_fp && demo_header_read(demo_fp, &demo_replay))
387 strncpy(demo_replay.filename, name, MAXSTR);
388 strncpy(demo_replay.name,
389 base_name(text_from_locale(demo_replay.filename), REPLAY_EXT),
392 if (!demo_load_level(&demo_replay, &demo_level_replay))
397 lg->mode = demo_replay.mode;
398 lg->score = demo_replay.score;
399 lg->times = demo_replay.times;
400 lg->time = demo_replay.time;
401 lg->goal = demo_replay.goal;
403 /* A normal replay demo */
404 audio_music_fade_to(0.5f, demo_level_replay.song);
405 return game_init(&demo_level_replay, demo_replay.time,
408 else /* A title screen demo */
409 return game_init(&demo_level_replay, demo_replay.time, 0);
414 int demo_replay_step(float dt)
416 const float gdn[3] = { 0.0f, -9.8f, 0.0f };
417 const float gup[3] = { 0.0f, +9.8f, 0.0f };
421 if (input_get(demo_fp))
423 /* Play out current game state. */
428 demo_status = game_step(gdn, dt, 1); break;
430 (void) game_step(gup, dt, 0); break;
432 (void) game_step(gdn, dt, 0); break;
441 void demo_replay_stop(int d)
448 if (d) remove(demo_replay.filename);
452 void demo_replay_dump_info(void)
454 demo_dump_info(&demo_replay);
457 /*---------------------------------------------------------------------------*/