75cca35e667aangolmois.c← 84:1c54d34cca9a |
85:75cca35e667a |
87:4cf3f3a15bec → |
-rw-r--r--massive option restructuring; now possible to map multiple keys to single virtual key; beep sound for speed adjustment; new survival gauge criteria (the original was waaaaaaaay easy); other small fixes and adjustments.
1 /* 2 * Angolmois -- the simple BMS player 3 * Copyright (c) 2005, 2007, 2009, 2012, Kang Seonghoon. 4 * Project Angolmois is copyright (c) 2003-2007, Choi Kaya (CHKY). 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 2 9 * of the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 */ 20 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <stdarg.h> 25 #include <time.h> 26 #include <SDL.h> 27 #include <SDL_mixer.h> 28 #include <SDL_image.h> 29 #include <smpeg.h> 30 31 static const char VERSION[] = "Angolmois 2.0.0 alpha 1"; 32 static const char *argv0 = "angolmois"; 33 34 /******************************************************************************/ 35 /* constants, variables */ 36 37 #define ARRAYSIZE(x) (sizeof(x)/sizeof(*x)) 38 #define SWAP(x,y,t) {(t)=(x);(x)=(y);(y)=(t);} 39 40 static int opt_mode = 0, opt_showinfo = 1, opt_fullscreen = 1, opt_random = 0, opt_bga = 0; 41 42 static const char *bmsheader[] = { 43 "title", "genre", "artist", "stagefile", "bpm", "player", "playlevel", 44 "rank", "lntype", "lnobj", "wav", "bmp", "bga", "stop", "stp", "random", 45 "if", "else", "endif", 0}; 46 static char *bmspath, respath[512]; 47 static char **bmsline = NULL; 48 static int nbmsline = 0; 49 50 static char metadata[4][1024]; 51 static double bpm = 130; 52 static int value[5] = {1, 0, 2, 1, 0}; 53 #define v_player value[0] 54 #define v_playlevel value[1] 55 #define v_rank value[2] 56 #define lntype value[3] 57 #define lnobj value[4] 58 59 static char *paths[2][1296]; 60 #define sndpath paths[0] 61 #define imgpath paths[1] 62 static int (*blitcmd)[8] = NULL, nblitcmd = 0; 63 static Mix_Chunk *sndres[1296]; 64 static SDL_Surface *imgres[1296]; 65 static int stoptab[1296] = {0}; 66 static double bpmtab[1296] = {0}; 67 static SMPEG *mpeg = NULL; 68 int imgmpeg = -2; 69 70 typedef struct { 71 double time; /* time */ 72 int type; /* for notes: start(0) and end(1) of longnote, visible(2), invisible(3) 73 * for BGA: lower layer(0), upper layer(1), poor BGA(2) 74 * for BPM: direct(0), indirect(1) 75 * for STOP: unit=1/192 measure, indirect(0), unit=msec, direct(1) 76 * otherwise: always 0. note that removed one has type -1. */ 77 int index; /* associated resource or key value (if any) */ 78 } bmsnote; 79 80 static bmsnote *channel[22]; /* 0..17: notes, 18: BGM; 19: BGA; 20: BPM; 21: STOP */ 81 static double _shorten[2005], *shorten = _shorten + 1; 82 static int nchannel[22] = {0}; 83 static double length; 84 85 /******************************************************************************/ 86 /* system dependent functions */ 87 88 #ifdef WIN32 89 90 #include <windows.h> 91 92 static const char sep = '\\'; 93 94 static int filedialog(char *buf) 95 { 96 OPENFILENAME ofn = { 97 .lStructSize = sizeof ofn, 98 .lpstrFilter = 99 "All Be-Music Source File (*.bms;*.bme;*.bml)\0*.bms;*.bme;*.bml\0" 100 "Be-Music Source File (*.bms)\0*.bms\0" 101 "Extended Be-Music Source File (*.bme)\0*.bme\0" 102 "Longnote Be-Music Source File (*.bml)\0*.bml\0" 103 "All Files (*.*)\0*.*\0", 104 .lpstrFile = buf, 105 .nMaxFile = 512, 106 .lpstrTitle = "Choose a file to play", 107 .Flags = OFN_HIDEREADONLY}; 108 return GetOpenFileName(&ofn); 109 } 110 111 static void die(const char *msg, ...) 112 { 113 va_list v; 114 char b[512]; 115 va_start(v, msg); 116 vsprintf(b, c, v); 117 va_end(a); 118 MessageBox(0, b, VERSION, 0); 119 exit(1); 120 } 121 122 static void dirinit(void) {} 123 static void dirfinal(void) {} 124 static const char *adjust_path_case(char *file) { return file; } 125 126 #else /* WIN32 */ 127 128 #include <dirent.h> 129 130 static const char sep = '/'; 131 static char *flist[2592]; 132 static int nfiles = 0; 133 134 static int filedialog(char *buf) 135 { 136 return 0; 137 } 138 139 static void die(const char *msg, ...) 140 { 141 va_list v; 142 fprintf(stderr, "%s: ", argv0); 143 va_start(v, msg); 144 vfprintf(stderr, msg, v); 145 va_end(v); 146 fprintf(stderr, "\n"); 147 exit(1); 148 } 149 150 static int stricmp(const char *a, const char *b); /* DUMMY */ 151 static char *strcopy(const char*); /* DUMMY */ 152 153 static void dirinit(void) 154 { 155 DIR *d; 156 struct dirent *e; 157 158 if (d = opendir(bmspath)) { 159 while (e = readdir(d)) 160 flist[nfiles++] = strcopy(e->d_name); 161 closedir(d); 162 } 163 } 164 165 static void dirfinal(void) 166 { 167 while (nfiles--) free(flist[nfiles]); 168 } 169 170 static const char *adjust_path_case(char *file) 171 { 172 int i; 173 for (i = 0; i < nfiles; ++i) { 174 if (stricmp(flist[i], file)) return flist[i]; 175 } 176 return ""; /* always nonexistent file */ 177 } 178 #endif 179 180 /******************************************************************************/ 181 /* general functions */ 182 183 static int lcase(char c) 184 { 185 return ((c|32)-19)/26-3 ? c : c|32; 186 } 187 188 static int stricmp(const char *a, const char *b) 189 { 190 while (*a && *b && lcase(*a) == lcase(*b)) ++a, ++b; 191 return *a == *b; 192 } 193 194 static char *adjust_path(char *path) 195 { 196 strcpy(respath, bmspath); 197 strcat(respath, adjust_path_case(path)); /* XXX could be overflow */ 198 return respath; 199 } 200 201 static char *adjust_path_with_ext(char *path, char *ext) 202 { 203 int len = strlen(bmspath); 204 char *oldext; 205 strcpy(respath, bmspath); 206 strcpy(respath + len, path); 207 oldext = strrchr(respath + len, '.'); 208 if (oldext) { 209 strcpy(oldext, ext); 210 strcpy(respath + len, adjust_path_case(respath + len)); 211 } 212 return respath; 213 } 214 215 static char *strcopy(const char *src) 216 { 217 char *dest = malloc(strlen(src)+1); 218 return strcpy(dest, src); 219 } 220 221 /******************************************************************************/ 222 /* bms parser */ 223 224 #define GET_CHANNEL(player, chan) ((player)*9+(chan)-1) 225 #define ADD_NOTE(player, chan, time, index) \ 226 add_note(GET_CHANNEL(player,chan), (time), 2/*NOTE*/, (index)) 227 #define ADD_INVNOTE(player, chan, time, index) \ 228 add_note(GET_CHANNEL(player,chan), (time), 3/*INVNOTE*/, (index)) 229 #define ADD_LNDONE(player, chan, time, index) \ 230 add_note(GET_CHANNEL(player,chan), (time), 0/*LNDONE*/, (index)) 231 #define ADD_LNSTART(player, chan, time, index) \ 232 add_note(GET_CHANNEL(player,chan), (time), 1/*LNSTART*/, (index)) 233 #define ADD_BGM(time, index) \ 234 add_note(18, (time), 0, (index)) 235 #define ADD_BGA(time, index) \ 236 add_note(19, (time), 0, (index)) 237 #define ADD_BGA2(time, index) \ 238 add_note(19, (time), 1, (index)) 239 #define ADD_POORBGA(time, index) \ 240 add_note(19, (time), 2, (index)) 241 #define ADD_BPM(time, index) \ 242 add_note(20, (time), 0, (index)) 243 #define ADD_BPM2(time, index) \ 244 add_note(20, (time), 1, (index)) 245 #define ADD_STOP(time, index) \ 246 add_note(21, (time), 0, (index)) 247 #define ADD_STP(time, index) \ 248 add_note(21, (time), 1, (index)) 249 250 static int getdigit(int n) 251 { 252 return 47<n && n<58 ? n-48 : ((n|32)-19)/26==3 ? (n|32)-87 : -1296; 253 } 254 255 static int key2index(const char *a) 256 { 257 return getdigit(*a) * 36 + getdigit(a[1]); 258 } 259 260 static int compare_bmsline(const void *a, const void *b) 261 { 262 int i, j; 263 for (i = 0; i < 6; ++i) { 264 if (j = (*(char**)a)[i] - (*(char**)b)[i]) return j; 265 } 266 return 0; 267 } 268 269 static int compare_bmsnote(const void *a, const void *b) 270 { 271 const bmsnote *A = a, *B = b; 272 return (A->time > B->time ? 1 : A->time < B->time ? -1 : A->type - B->type); 273 } 274 275 static void add_note(int chan, double time, int type, int index) 276 { 277 bmsnote temp = {time, type, index}; 278 channel[chan] = realloc(channel[chan], sizeof(bmsnote) * (nchannel[chan]+1)); 279 channel[chan][nchannel[chan]++] = temp; 280 } 281 282 static void remove_note(int chan, int index) 283 { 284 if (chan < 18 && channel[chan][index].index) { 285 ADD_BGM(channel[chan][index].time, channel[chan][index].index); 286 } 287 channel[chan][index].type = -1; 288 } 289 290 #define KEY_PATTERN "%2[0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]" 291 292 static int parse_bms(void) 293 { 294 FILE *fp; 295 int i, j, k; 296 int rnd = 1, ignore = 0; 297 double t; 298 char linebuf[4096], buf1[4096], buf2[4096]; 299 char *line = linebuf; 300 301 fp = fopen(bmspath, "r"); 302 if (!fp) return 1; 303 304 srand(time(0)); 305 while (fgets(line = linebuf, sizeof linebuf, fp)) { 306 if (line[0] != '#') continue; 307 ++line; 308 309 for (i = 0; bmsheader[i]; ++i) { 310 for (j = 0; bmsheader[i][j]; ++j) 311 if (((bmsheader[i][j] ^ line[j]) | 32) != 32) break; 312 if (!bmsheader[i][j]) break; 313 } 314 315 switch (i) { 316 case 15: /* random */ 317 if (sscanf(line+j, "%*[ ]%d", &j) >= 1) { 318 rnd = rand() % abs(j) + 1; 319 } 320 break; 321 322 case 16: /* if */ 323 if (sscanf(line+j, "%*[ ]%d", &j) >= 1) { 324 ignore = (rnd != j); 325 } 326 break; 327 328 case 17: /* else */ 329 ignore = !ignore; 330 break; 331 332 case 18: /* endif */ 333 ignore = 0; 334 break; 335 } 336 337 if (!ignore) { 338 switch (i) { 339 case 0: /* title */ 340 case 1: /* genre */ 341 case 2: /* artist */ 342 case 3: /* stagefile */ 343 sscanf(line+j, "%*[ ]%[^\r\n]", metadata[i]); 344 break; 345 346 case 4: /* bpm */ 347 if (sscanf(line+j, "%*[ ]%lf", &bpm) >= 1) { 348 /* do nothing, bpm is set */ 349 } else if (sscanf(line+j, KEY_PATTERN "%*[ ]%lf", buf1, &t) >= 2) { 350 i = key2index(buf1); 351 if (i >= 0) bpmtab[i] = t; 352 } 353 break; 354 355 case 5: /* player */ 356 case 6: /* playlevel */ 357 case 7: /* rank */ 358 case 8: /* lntype */ 359 sscanf(line+j, "%*[ ]%d", &value[i-5]); 360 break; 361 362 case 9: /* lnobj */ 363 if (sscanf(line+j, "%*[ ]" KEY_PATTERN, buf1) >= 1) { 364 lnobj = key2index(buf1); 365 } 366 break; 367 368 case 10: /* wav## */ 369 case 11: /* bmp## */ 370 if (sscanf(line+j, KEY_PATTERN "%*[ ]%[^\r\n]", buf1, buf2) >= 2) { 371 j = key2index(buf1); 372 if (j >= 0) { 373 free(paths[i-10][j]); 374 paths[i-10][j] = strcopy(buf2); 375 } 376 } 377 break; 378 379 case 12: /* bga## */ 380 i = nblitcmd; 381 blitcmd = realloc(blitcmd, sizeof(int) * 8 * (i+1)); 382 if (sscanf(line+j, KEY_PATTERN "%*[ ]" KEY_PATTERN "%*[ ]%d %d %d %d %d %d", 383 buf1, buf2, blitcmd[i]+2, blitcmd[i]+3, blitcmd[i]+4, 384 blitcmd[i]+5, blitcmd[i]+6, blitcmd[i]+7) >= 8) { 385 blitcmd[i][0] = key2index(buf1); 386 blitcmd[i][1] = key2index(buf2); 387 if (blitcmd[i][0] >= 0 && blitcmd[i][1] >= 0) ++nblitcmd; 388 } 389 break; 390 391 case 13: /* stop## */ 392 if (sscanf(line+j, KEY_PATTERN "%*[ ]%d", buf1, &j) >= 2) { 393 i = key2index(buf1); 394 if (i >= 0) stoptab[i] = j; 395 } 396 break; 397 398 case 14: /* stp## */ 399 if (sscanf(line+j, "%d.%d %d", &i, &j, &k) >= 3) { 400 ADD_STP(i+j/1e3, k); 401 } 402 break; 403 404 case 19: /* #####:... */ 405 /* only check validity, do not store them yet */ 406 if (sscanf(line+j, "%*5c:%c", buf1) >= 1) { 407 bmsline = realloc(bmsline, sizeof(char*) * (nbmsline+1)); 408 bmsline[nbmsline] = strcopy(line); 409 ++nbmsline; 410 } 411 } 412 } 413 } 414 fclose(fp); 415 416 return 0; 417 } 418 419 static int parse_bmsline(void) 420 { 421 int prev[40] = {0}; 422 int i, j, k, a, b, c; 423 int measure, chan; 424 double t; 425 426 qsort(bmsline, nbmsline, sizeof(char*), compare_bmsline); 427 428 for (i = 0; i < nbmsline; ++i) { 429 j = atoi(bmsline[i]); 430 measure = j / 100; 431 chan = j % 100; 432 if (chan == 2) { 433 shorten[measure] = atof(bmsline[i]+6); 434 } else { 435 j = 6 + strspn(bmsline[i]+6, " \t\r\n"); 436 a = strcspn(bmsline[i]+j, " \t\r\n") / 2; 437 for (k = 0; k < a; ++k, j+=2) { 438 b = key2index(bmsline[i]+j); 439 t = measure + 1. * k / a; 440 if (b) { 441 if (chan == 1) { 442 ADD_BGM(t, b); 443 } else if (chan == 3 && (b/36<16 && b%36<16)) { 444 ADD_BPM(t, b/36*16+b%36); 445 } else if (chan == 4) { 446 ADD_BGA(t, b); 447 } else if (chan == 6) { 448 ADD_POORBGA(t, b); 449 } else if (chan == 7) { 450 ADD_BGA2(t, b); 451 } else if (chan == 8) { 452 ADD_BPM2(t, b); 453 } else if (chan == 9) { 454 ADD_STOP(t, b); 455 } else if (chan % 10 != 0 && chan > 9 && chan < 30) { 456 if (lnobj && b == lnobj) { 457 c = GET_CHANNEL(chan>20, chan%10); 458 if (nchannel[c] && channel[c][nchannel[c]-1].type==2/*NOTE*/) { 459 channel[c][nchannel[c]-1].type = 1/*LNSTART*/; 460 ADD_LNDONE(chan>20, chan%10, t, b); 461 } 462 } else { 463 ADD_NOTE(chan>20, chan%10, t, b); 464 } 465 } else if (chan % 10 != 0 && chan > 29 && chan < 50) { 466 ADD_INVNOTE(chan>40, chan%10, t, b); 467 } 468 } 469 if (chan % 10 != 0 && chan > 49 && chan < 70) { 470 if (lntype == 1 && b) { 471 if (prev[chan-50]) { 472 prev[chan-50] = 0; 473 ADD_LNDONE(chan>60, chan%10, t, 0); 474 } else { 475 prev[chan-50] = b; 476 ADD_LNSTART(chan>60, chan%10, t, b); 477 } 478 } else if (lntype == 2) { 479 if (prev[chan-50] || prev[chan-50] != b) { 480 if (prev[chan-50]) { 481 if (prev[chan-30] + 1 < measure) { 482 ADD_LNDONE(chan>60, chan%10, prev[chan-30]+1, 0); 483 } else if (prev[chan-50] != b) { 484 ADD_LNDONE(chan>60, chan%10, t, 0); 485 } 486 } 487 if (b && (prev[chan-50]!=b || prev[chan-30]+1<measure)) { 488 ADD_LNSTART(chan>60, chan%10, t, b); 489 } 490 prev[chan-30] = measure; 491 prev[chan-50] = b; 492 } 493 } 494 } 495 } 496 } 497 free(bmsline[i]); 498 } 499 free(bmsline); 500 length = measure + 2; 501 for (i = 0; i < 20; ++i) { 502 if (prev[i]) { 503 if (lntype == 2 && prev[i+20] + 1 < measure) { 504 ADD_LNDONE(i>10, i%10, prev[i+20]+1, 0); 505 } else { 506 ADD_LNDONE(i>10, i%10, length - 1, 0); 507 } 508 } 509 } 510 511 return 0; 512 } 513 514 static int sanitize_bmsline(void) 515 { 516 int i, j, k, b, c; 517 518 for (i = 0; i < 22; ++i) { 519 if (!channel[i]) continue; 520 qsort(channel[i], nchannel[i], sizeof(bmsnote), compare_bmsnote); 521 522 if (i != 18 && i < 21) { 523 b = 0; 524 j = 0; 525 while (j < nchannel[i]) { 526 k = j; 527 c = 0; 528 for (k = j; k < nchannel[i] && channel[i][k].time <= channel[i][j].time; ++k) { 529 if (c & 1 << channel[i][k].type) { 530 remove_note(i, k); 531 } else { 532 c |= 1 << channel[i][k].type; 533 } 534 } 535 536 if (b) { 537 /* remove starting longnote if there's no ending longnote */ 538 c &= ~(c & 1 ? 0 : 2); 539 /* remove visible note */ 540 c &= ~4; 541 /* remove invisible note if there is any longnote */ 542 c &= ~(c & 3 ? 8 : 0); 543 544 b = !(c & 1); 545 } else { 546 /* remove starting longnote if there's also ending longnote */ 547 c &= ~(c & 1 ? 2 : 0); 548 /* remove ending longnote */ 549 c &= ~1; 550 /* keep only one note, in the order of importance */ 551 c &= -c; 552 553 b = (c & 2); 554 } 555 556 for (; j < k; ++j) { 557 if (channel[i][j].type < 0) continue; 558 559 if (i < 18 && !(c & 1 << channel[i][j].type)) { 560 remove_note(i, j); 561 } 562 } 563 } 564 if (i < 18 && b) { 565 /* remove last starting longnote which is unfinished */ 566 while (j >= 0 && channel[i][--j].type < 0); 567 if (j >= 0 && channel[i][j].type == 1/*LNSTART*/) remove_note(i, j); 568 } 569 } 570 k = 0; 571 for (j = 0; j < nchannel[i]; ++j) { 572 if (channel[i][j].type >= 0) { 573 channel[i][j-k] = channel[i][j]; 574 } else { 575 ++k; 576 } 577 } 578 nchannel[i] -= k; 579 } 580 581 for (i = 0; i < 2005; ++i) { 582 if (_shorten[i] <= .001) 583 _shorten[i] = 1; 584 } 585 586 return 0; 587 } 588 589 static int read_bms(void) 590 { 591 if (parse_bms()) return 1; 592 if (parse_bmsline()) return 1; 593 if (sanitize_bmsline()) return 1; 594 595 return 0; 596 } 597 598 static SDL_Surface *newsurface(int f, int w, int h); /* DUMMY */ 599 static SDL_Rect *newrect(int x, int y, int w, int h); /* DUMMY */ 600 601 static int load_resource(void (*callback)(const char *)) 602 { 603 SDL_Surface *temp; 604 int i; 605 606 for (i = 0; i < 1296; ++i) { 607 if (sndpath[i]) { 608 if (callback) callback(sndpath[i]); 609 sndres[i] = Mix_LoadWAV(adjust_path(sndpath[i])); 610 if (!sndres[i]) sndres[i] = Mix_LoadWAV(adjust_path_with_ext(sndpath[i], ".mp3")); 611 if (!sndres[i]) sndres[i] = Mix_LoadWAV(adjust_path_with_ext(sndpath[i], ".ogg")); 612 free(sndpath[i]); 613 sndpath[i] = 0; 614 } 615 if (imgpath[i]) { 616 char *ext = strrchr(imgpath[i], '.'); 617 if (callback) callback(imgpath[i]); 618 if (ext && stricmp(ext, ".mpg") && opt_bga < 1 && !mpeg) { 619 mpeg = SMPEG_new(adjust_path(imgpath[i]), NULL, 0); 620 imgmpeg = i; 621 } else if (opt_bga < 2) { 622 temp = IMG_Load(adjust_path(imgpath[i])); 623 if (!temp) temp = IMG_Load(adjust_path_with_ext(imgpath[i], ".png")); 624 if (!temp) temp = IMG_Load(adjust_path_with_ext(imgpath[i], ".jpg")); 625 if (temp) { 626 imgres[i] = SDL_DisplayFormat(temp); 627 SDL_FreeSurface(temp); 628 SDL_SetColorKey(imgres[i], SDL_SRCCOLORKEY|SDL_RLEACCEL, 0); 629 } 630 } 631 free(imgpath[i]); 632 imgpath[i] = 0; 633 } 634 } 635 636 for (i = 0; i < nblitcmd; ++i) { 637 if (blitcmd[i][0]<0 || blitcmd[i][1]<0 || !imgres[blitcmd[i][1]]) continue; 638 temp = imgres[blitcmd[i][0]]; 639 if (!temp) { 640 imgres[blitcmd[i][0]] = temp = newsurface(SDL_SWSURFACE, 256, 256); 641 SDL_FillRect(temp, 0, SDL_MapRGB(imgres[blitcmd[i][0]]->format, 0, 0, 0)); 642 SDL_SetColorKey(temp, SDL_SRCCOLORKEY|SDL_RLEACCEL, 0); 643 } 644 if (blitcmd[i][2] < 0) blitcmd[i][2] = 0; 645 if (blitcmd[i][3] < 0) blitcmd[i][3] = 0; 646 if (blitcmd[i][4] > blitcmd[i][2] + 256) blitcmd[i][4] = blitcmd[i][2] + 256; 647 if (blitcmd[i][5] > blitcmd[i][3] + 256) blitcmd[i][5] = blitcmd[i][3] + 256; 648 SDL_BlitSurface(imgres[blitcmd[i][1]], 649 newrect(blitcmd[i][2], blitcmd[i][3], blitcmd[i][4]-blitcmd[i][2], blitcmd[i][5]-blitcmd[i][3]), 650 temp, newrect(blitcmd[i][6], blitcmd[i][7], 0, 0)); 651 } 652 free(blitcmd); 653 654 return 0; 655 } 656 657 static int xflag, xnnotes, xscore; /* DUMMY */ 658 659 /* 660 bit 0: it uses 7 keys? 661 bit 1: it uses long-note? 662 bit 2: it uses pedal? 663 bit 3: it has bpm variation? 664 */ 665 static void get_bms_info(void) 666 { 667 int i, j; 668 669 xflag = xnnotes = 0; 670 if (nchannel[7] || nchannel[8] || nchannel[16] || nchannel[17]) xflag |= 1; 671 if (nchannel[6] || nchannel[15]) xflag |= 4; 672 if (nchannel[20]) xflag |= 8; 673 for (i = 0; i < 18; ++i) { 674 for (j = 0; j < nchannel[i]; ++j) { 675 if (channel[i][j].type == 1/*LNSTART*/) { 676 xflag |= 2; 677 ++xnnotes; 678 } else if (channel[i][j].type == 2/*NOTE*/) { 679 ++xnnotes; 680 } 681 } 682 } 683 for (i = 0; i < xnnotes; ++i) { 684 xscore += (int)(300 * (1 + 1. * i / xnnotes)); 685 } 686 } 687 688 static double adjust_object_position(double base, double time); /* DUMMY */ 689 690 static int pcur[22]; /* DUMMY */ 691 692 static int get_bms_duration(void) 693 { 694 int i, j, time, rtime, ttime; 695 double pos, xbpm, tmp; 696 697 xbpm = bpm; 698 time = rtime = 0; 699 pos = -1; 700 while (1) { 701 for (i = 0, j = -1; i < 22; ++i) { 702 if (pcur[i] < nchannel[i] && (j < 0 || channel[i][pcur[i]].time < channel[j][pcur[j]].time)) j = i; 703 } 704 if (j < 0) { 705 time += (int)(adjust_object_position(pos, length) * 24e7 / xbpm); 706 break; 707 } 708 time += (int)(adjust_object_position(pos, channel[j][pcur[j]].time) * 24e7 / xbpm); 709 710 i = channel[j][pcur[j]].index; 711 ttime = 0; 712 if (j < 19) { 713 if (sndres[i]) ttime = (int)(sndres[i]->alen / .1764); 714 } else if (j == 20) { 715 tmp = (channel[j][pcur[j]].type ? bpmtab[i] : i); 716 if (tmp > 0) { 717 xbpm = tmp; 718 } else if (tmp < 0) { 719 time += (int)(adjust_object_position(-1, pos) * 24e7 / xbpm); 720 break; 721 } 722 } else if (j == 21) { 723 if (channel[j][pcur[j]].type) { 724 time += i; 725 } else { 726 time += (int)(stoptab[i] * 125e4 * shorten[(int)(pos+1)-1] / xbpm); 727 } 728 } 729 if (rtime < time + ttime) rtime = time + ttime; 730 pos = channel[j][pcur[j]].time; 731 ++pcur[j]; 732 } 733 return (time > rtime ? time : rtime) / 1000; 734 } 735 736 static void clone_bms(void) 737 { 738 int i; 739 740 for (i = 0; i < 9; ++i) { 741 free(channel[i+9]); 742 nchannel[i+9] = nchannel[i]; 743 channel[i+9] = malloc(sizeof(bmsnote) * nchannel[i]); 744 memcpy(channel[i+9], channel[i], sizeof(bmsnote) * nchannel[i]); 745 } 746 } 747 748 static void shuffle_bms(int mode, int player) 749 { 750 bmsnote *tempchan, *result[18] = {0}; 751 int nresult[18] = {0}; 752 int map[18], perm[18], nmap; 753 int ptr[18] = {0,}, thru[18] = {0}, target[18]; 754 int thrumap[18], thrurmap[18]; 755 int i, j, flag = 1, temp; 756 double t; 757 758 srand(time(0)); 759 for (i = 0; i < 18; ++i) map[i] = perm[i] = i; 760 if (!nchannel[7] && !nchannel[8] && !nchannel[16] && !nchannel[17]) { 761 map[7] = map[8] = map[16] = map[17] = -1; 762 } 763 if (!nchannel[6] && !nchannel[15]) { 764 map[6] = map[15] = -1; 765 } 766 if (mode != 3 && mode != 5) { 767 map[5] = map[6] = map[14] = map[15] = -1; 768 } 769 if (player) { 770 for (i = 0; i < 9; ++i) map[player==1 ? i+9 : i] = -1; 771 } 772 for (i = j = 0; i < 18; ++i) { 773 if (map[i] < 0) { 774 ++j; 775 } else { 776 map[i-j] = map[i]; 777 } 778 } 779 nmap = 18 - j; 780 781 if (mode < 2) { /* mirror */ 782 for (i = 0, j = nmap-1; i < j; ++i, --j) { 783 SWAP(channel[map[i]], channel[map[j]], tempchan); 784 SWAP(nchannel[map[i]], nchannel[map[j]], temp); 785 } 786 } else if (mode < 4) { /* shuffle */ 787 for (i = nmap-1; i > 0; --i) { 788 j = rand() % i; 789 SWAP(channel[map[i]], channel[map[j]], tempchan); 790 SWAP(nchannel[map[i]], nchannel[map[j]], temp); 791 } 792 } else if (mode < 6) { /* random */ 793 while (flag) { 794 for (i = nmap-1; i > 0; --i) { 795 j = rand() % i; 796 SWAP(perm[i], perm[j], temp); 797 } 798 t = 1e4; 799 flag = 0; 800 for (i = 0; i < nmap; ++i) { 801 if (ptr[map[i]] < nchannel[map[i]]) { 802 flag = 1; 803 target[i] = 1; 804 if (t > channel[map[i]][ptr[map[i]]].time) 805 t = channel[map[i]][ptr[map[i]]].time - 1e-4; 806 } else { 807 target[i] = 0; 808 } 809 } 810 t += 2e-4; 811 for (i = 0; i < nmap; ++i) { 812 if (target[i] && channel[map[i]][ptr[map[i]]].time > t) 813 target[i] = 0; 814 } 815 for (i = 0; i < nmap; ++i) { 816 if (!target[i]) continue; 817 temp = channel[map[i]][ptr[map[i]]].type; 818 if (temp == 0/*LNDONE*/) { 819 j = thrumap[i]; 820 thru[j] = 2; 821 } else { 822 j = perm[i]; 823 while (thru[j]) j = perm[thrurmap[j]]; 824 if (temp == 1/*LNSTART*/) { 825 thrumap[i] = j; 826 thrurmap[j] = i; 827 thru[j] = 1; 828 } 829 } 830 result[j] = realloc(result[j], sizeof(bmsnote) * (nresult[j]+1)); 831 result[j][nresult[j]++] = channel[map[i]][ptr[map[i]]++]; 832 } 833 for(i = 0; i < nmap; ++i) { 834 if (thru[i] == 2) thru[i] = 0; 835 } 836 } 837 for (i = 0; i < nmap; ++i) { 838 free(channel[map[i]]); 839 channel[map[i]] = result[i]; 840 nchannel[map[i]] = nresult[i]; 841 } 842 } 843 } 844 845 /******************************************************************************/ 846 /* general graphic functions */ 847 848 static SDL_Surface *screen; 849 static SDL_Event event; 850 static SDL_Rect rect[2]; 851 static int _rect=0; 852 853 static int getpixel(SDL_Surface *s, int x, int y) 854 { 855 Uint8 r, g, b; 856 SDL_GetRGB(((Uint32*)s->pixels)[x+y*s->pitch/4], s->format, &r, &g, &b); 857 return (int)(r << 16) | ((int)g << 8) | (int)b; 858 } 859 860 static Uint32 map(int c) 861 { 862 return SDL_MapRGB(screen->format, c >> 16, (c >> 8) & 255, c & 255); 863 } 864 865 static int putpixel(SDL_Surface *s, int x, int y, int c) 866 { 867 c = SDL_MapRGB(s->format, c >> 16, (c >> 8) & 255, c & 255); 868 return ((Uint32*)s->pixels)[x+y*s->pitch/4] = c; 869 } 870 871 static int blend(int x, int y, int a, int b) 872 { 873 int i = 0; 874 for (; i < 24; i += 8) 875 y += ((x>>i&255) - (y>>i&255))*a/b << i; 876 return y; 877 } 878 879 static void putblendedpixel(SDL_Surface *s, int x, int y, int c, int o) 880 { 881 putpixel(s, x, y, blend(getpixel(s,x,y), c, o, 255)); 882 } 883 884 static SDL_Surface *newsurface(int f, int w, int h) 885 { 886 return SDL_CreateRGBSurface(f, w, h, 32, 0xff0000, 0xff00, 0xff, 0); 887 } 888 889 static SDL_Rect *newrect(int x, int y, int w, int h) 890 { 891 SDL_Rect *r = rect+_rect++%2; 892 r->x = x; 893 r->y = y; 894 r->w = w; 895 r->h = h; 896 return r; 897 } 898 899 static int bicubic_kernel(int x, int y) { 900 if (x < 0) x = -x; 901 if (x < y) { 902 return ((y*y - 2*x*x + x*x/y*x) << 11) / y / y; 903 } else if (x < y * 2) { 904 return ((4*y*y - 8*x*y + 5*x*x - x*x/y*x) << 11) / y / y; 905 } else { 906 return 0; 907 } 908 } 909 910 static int bicubic_interpolation(SDL_Surface *src, SDL_Surface *dest) { 911 int x, dx, y, dy, ww, hh, w, h; 912 int i, j, k, r, g, b, c, xx, yy, a[4][2], d; 913 914 dx = x = 0; 915 ww = src->w - 1; 916 hh = src->h - 1; 917 w = dest->w - 1; 918 h = dest->h - 1; 919 for (i = 0; i <= w; ++i) { 920 dy = y = 0; 921 for (j = 0; j <= h; ++j) { 922 r = g = b = 0; 923 for (k = 0; k < 4; ++k) { 924 a[k][0] = bicubic_kernel((x+k-1)*w - i*ww, w); 925 a[k][1] = bicubic_kernel((y+k-1)*h - j*hh, h); 926 } 927 for (k = 0; k < 16; ++k) { 928 xx = x + k/4 - 1; 929 yy = y + k%4 - 1; 930 if (xx>=0 && xx<=ww && yy>=0 && yy<=hh) { 931 c = getpixel(src, xx, yy); 932 d = a[k/4][0] * a[k%4][1] >> 6; 933 r += (c>>16) * d; 934 g += (c>>8&255) * d; 935 b += (c&255) * d; 936 } 937 } 938 r = (r<0 ? 0 : r>>24 ? 255 : r>>16); 939 g = (g<0 ? 0 : g>>24 ? 255 : g>>16); 940 b = (b<0 ? 0 : b>>24 ? 255 : b>>16); 941 putpixel(dest, i, j, (r<<16) | (g<<8) | b); 942 943 dy += hh; 944 if (dy > h) { 945 ++y; 946 dy -= h; 947 } 948 } 949 dx += ww; 950 if (dx > w) { 951 ++x; 952 dx -= w; 953 } 954 } 955 956 return 0; 957 } 958 959 /******************************************************************************/ 960 /* font functions */ 961 962 static int fontdatawords[] = {0, 0, 2, 6, 2, 5, 32, 96, 97, 15, 497, 15, 963 1521, 15, 1537, 16, 48, 176, 1, 3, 1, 3, 7, 1, 4080, 4096, 3, 1, 8, 964 3, 4097, 4080, 16, 16128, 240, 1, 2, 9, 3, 8177, 15, 16385, 240, 15, 965 1, 47, 721, 143, 2673, 2, 6, 7, 1, 31, 17, 16, 63, 64, 33, 0, 1, 2, 966 1, 8, 3}; 967 static const char *fontdataindices = 968 "!!7a/&w7a!!g'M*Qg(O&J!!&Je!!g2Qg-B!!u2cQ[b7b2[[Q7b2[bQ!c!i&d>UT2c&" 969 "b>WT!b2eGW&!c!i2MbUbD!.8(M$UQCb-b!!l'U*2eTXb2Gb5b>9ZW!!k*e8(J$b!!u" 970 "*8(M$Q!h#b'O*?!!k#Q'O*?b!h8(M$Q!!m&JTLcG_&J>]TLc&J!!o&Je7[&Je!!{&J" 971 "dM$Q!!s7[!!}e&Je!!m0b3b.8(M$U!Cb-b!o>UQ2Ub]ba9Y[SbQCb2GW!!k*MbQbBb" 972 "!kaQ!!k>UQ2Ub]bG8.M(U$[!Ca[!!k>UQ2d!IJQ!d2cGW!!k78bM6U2Cb2ea[2!c!l" 973 "a[!2e>[Q!d2cGW!!k>UQ2c!b>[Q2gGW!!ka[Q!UbDb.8(J&h!!k>UQ2eIXQ2gGW!!k" 974 ">UQ2gaWQ!b2cGW!!m&Je!!e&Je!!m&Je!!e&JdM$Q!!k3b.8(M$U!H#W'O,?4!!t7[" 975 "!!c7[!!r:#W'O,?5!.8(M$U!Cb!k>UQ2cUbD!.8(J!!&Jc!!k>UJ)LKKhGb!)7W!!k" 976 "'8,M=UWCQ2ca[Q2e!!k>[Q2eI[Q2gG[!!k>MSUQC!2gQ:RWGO!!k,[<2WbQhUbDb.[" 977 "!!ka[!2e7[!2ga[!!ka[!2e7[!2j!k>MSUQC!2c[bWbQ:RWGO!!kQ2ga[Q2i!!k7[&" 978 "Jo7[!!kQm2eGW!!kU2Db.9([$b#b'b,@<2Wb!!l2qa[!!kU:^[adT2cQh!!kQ2d:R[" 979 "Vba@_2WbQd!!k,M=UWCQ2gU:EW.O!!k>[Q2gG[!2h!k>UQ2iVbabIW_!Wb!j>[Q2gI" 980 "[Q2g!!k>UQ2c!b>WQ!d2cGW!!k7[&Jq!!kQ2qGW!!kQ2kU:EW.O(?!!kQ2gTfa[CW2" 981 "Q!!kM+U:^[GW.O,M>U`[WCO-!!kM'U,D<.W(O&Ji!!ka[Q!Ub]bG8.M(U$[!Ca[!!k" 982 "*Q!p*b!!n+b:#W'O,?4!1b!p*Qb!pQ!!g'8,M=UWCO-!!}oa[!!i#Q'O,?4!!}b>QQ" 983 "!caUQ2eaW!!l2e>[Q2iG[!!o>UQ2c!dQdGW!!kQfaUQ2iaW!!o>UQ2ea[!2UbGW!!k" 984 ">8TJc&b7[&Ji!!oaUQ2gaWQ!b2cGW!!f2e>[Q2m!!k&Jc!!&Jm!!kQd!dQi2eGW!!h" 985 "2cUbDb.9(['b,@<2Wb!!k*Jb?b!o!p;[abT2gQd!!o>[Q2m!!o>UQ2kGW!!o>[Q2kG" 986 "[!2f!iaUQ2kaWQ!e!j>[Q2c!k!o>UW2!b>WQ!d:GW!!k&Jc7[&JeTfG?!!oQ2mGW!!" 987 "oQ2gU:EW.O(?!!oQ2eTfa[FW!!oM+U:EW.O,M=UWCO-!!oQ2f:RWGO.A(M$U!Cb!ka" 988 "[]!G8.M(U$[!Ca[!!i78&Jg%[&Jg7?!!e&J}c!!c#[&Jg7A&Jg$[!!k#]NaaPG?!!}" 989 "o7[.W(O"; 990 991 static Uint16 fontdata[3072]; 992 static Uint8 (*zoomfont[16])[96] = {NULL}; 993 994 static void fontdecompress(void) 995 { 996 int i, ch; 997 998 for (i = ch = 0; i < ARRAYSIZE(fontdatawords); ++i) { 999 ch += fontdatawords[i]; 1000 fontdatawords[i] = ch; 1001 } 1002 for (i = 0; ch = *fontdataindices++; ) { 1003 if (ch > 97) { 1004 while (ch-- > 97) fontdata[i] = fontdata[i-2], ++i; 1005 } else if (ch > 32) { 1006 fontdata[i++] = fontdatawords[ch - 33]; 1007 } 1008 } 1009 } 1010 1011 static int _fontprocess(int x, int y, int z, int c, int s) 1012 { 1013 int i, j; 1014 for (i = 0; i < z; ++i) { 1015 for (j = (s==1 ? z-i : s==3 ? i+1 : 0); j < (s==2 ? i : s==4 ? z-i-1 : z); ++j) 1016 zoomfont[z][(y*z+i)*z+j][c] |= 1<<(7-x); 1017 } 1018 return z; 1019 } 1020 1021 static void fontprocess(int z) 1022 { 1023 int i, j, k, l, v; 1024 1025 zoomfont[z] = calloc(16*z*z, 96); 1026 for (i = l = 0; i < 96; ++i) { 1027 for (j = 0; j < 16; ++j, l+=2) { 1028 v = fontdata[l] << 16 | fontdata[l+1]; 1029 for (k = 0; k < 8; ++k, v >>= 4) { 1030 if ((v & 15) == 15) _fontprocess(k, j, z, i, 0); /* XXX? */ 1031 if (v & 1) _fontprocess(k, j, z, i, 1); /* /| */ 1032 if (v & 2) _fontprocess(k, j, z, i, 2); /* |\ */ 1033 if (v & 4) _fontprocess(k, j, z, i, 3); /* \| */ 1034 if (v & 8) _fontprocess(k, j, z, i, 4); /* |/ */ 1035 } 1036 } 1037 } 1038 } 1039 1040 static int printchar(SDL_Surface *s, int x, int y, int z, int c, int u, int v) 1041 { 1042 int i, j; 1043 1044 if (c != ' ' && c != '\t' && c != '\n' && c != '\r') { 1045 c -= (c<0 ? -96 : c<33 || c>126 ? c : 32); 1046 for (i = 0; i < 16*z; ++i) { 1047 for (j = 0; j < 8*z; ++j) { 1048 if (zoomfont[z][i*z+j%z][c] & (1<<(7-j/z))) 1049 putpixel(s, x+j, y+i, blend(u, v, i, 16*z-1)); 1050 } 1051 } 1052 } 1053 1054 return 8*z; 1055 } 1056 1057 static void printstr(SDL_Surface *s, int x, int y, int z, const char *c, int u, int v) 1058 { 1059 while (*c) { 1060 x += printchar(s, x, y, z, (Uint8)*c++, u, v); 1061 } 1062 } 1063 1064 /******************************************************************************/ 1065 /* main routines */ 1066 1067 static double playspeed = 1, targetspeed; 1068 static int now, origintime, starttime, stoptime = 0, adjustspeed = 0; 1069 static double startoffset = -1, startshorten = 1; 1070 static int xflag, xnnotes, xscore, xduration; 1071 static int pcur[22] = {0}, pfront[22] = {0}, prear[22] = {0}, pcheck[18] = {0}, thru[22] = {0}; 1072 static int bga[3] = {-1,-1,0}, poorbga = 0, bga_updated = 1; 1073 static int score = 0, scocnt[5] = {0}, scombo = 0, smaxcombo = 0; 1074 static double gradefactor; 1075 static int gradetime = 0, grademode, gauge = 256, survival = 150; 1076 1077 static SDL_Surface *sprite=0; 1078 static int keymap[SDLK_LAST]; /* -1: none, 0..17: notes, 18..19: speed down/up */ 1079 static int keypressed[18] = {0}; 1080 static int tkeyleft[18] = {41,67,93,119,145,0,223,171,197, 578,604,630,656,682,760,537,708,734}; 1081 static int tkeywidth[18] = {25,25,25,25,25,40,40,25,25, 25,25,25,25,25,40,40,25,25}; 1082 static int tpanel1 = 264, tpanel2 = 536, tbga = 272; 1083 #define WHITE 0x808080 1084 #define BLUE 0x8080ff 1085 static int tkeycolor[18] = { 1086 WHITE, BLUE, WHITE, BLUE, WHITE, 0xff8080, 0x80ff80, BLUE, WHITE, 1087 WHITE, BLUE, WHITE, BLUE, WHITE, 0xff8080, 0x80ff80, BLUE, WHITE}; 1088 #undef WHITE 1089 #undef BLUE 1090 static char *tgradestr[5] = {"MISS", "BAD", "GOOD", "GREAT", "COOL"}; 1091 static int tgradecolor[5][2] = { 1092 {0xff4040, 0xffc0c0}, {0xff40ff, 0xffc0ff}, {0xffff40, 0xffffc0}, 1093 {0x40ff40, 0xc0ffc0}, {0x4040ff, 0xc0c0ff}}; 1094 1095 static Mix_Chunk *beep; 1096 1097 static void check_exit(void) 1098 { 1099 while (SDL_PollEvent(&event)) { 1100 if (event.type == SDL_QUIT || (event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_ESCAPE)) exit(0); 1101 } 1102 } 1103 1104 static SDLKey get_sdlkey_from_name(const char *str) 1105 { 1106 SDLKey i; 1107 1108 /* XXX maybe won't work in SDL 1.3 */ 1109 for (i = SDLK_FIRST; i < SDLK_LAST; ++i) { 1110 if (stricmp(SDL_GetKeyName(i), str)) return i; 1111 } 1112 return SDLK_UNKNOWN; 1113 } 1114 1115 static void read_keymap(void) 1116 { 1117 static const struct { const char *name, *def; int base, limit; } envvars[] = { 1118 {"ANGOLMOIS_1P_KEYS", "z|s|x|d|c|left shift|left alt|f|v", 0, 9}, 1119 {"ANGOLMOIS_2P_KEYS", "m|k|,|l|.|right shift|right alt|;|/", 9, 18}, 1120 {"ANGOLMOIS_SPEED_KEYS", "f3|f4", 18, 20}, 1121 }; 1122 char *s, buf[256] = ""; 1123 int i, j, k, sep; 1124 SDLKey key; 1125 1126 for (i = 0; i < SDLK_LAST; ++i) keymap[i] = -1; 1127 for (i = 0; i < 3; ++i) { 1128 s = getenv(envvars[i].name); 1129 strncpy(buf, s ? s : envvars[i].def, sizeof(buf) - 1); 1130 s = buf; 1131 sep = 1; 1132 for (j = envvars[i].base; sep && j < envvars[i].limit; ) { 1133 k = strcspn(s, "%|"); /* both character is not used as a key name in SDL 1.2 */ 1134 sep = s[k]; 1135 s[k++] = '\0'; 1136 key = get_sdlkey_from_name(s); 1137 if (key == SDLK_UNKNOWN) { 1138 die("Unknown key name in the environment variable %s: %s", envvars[i].name, s); 1139 } 1140 keymap[key] = j; 1141 if (sep != '%') ++j; 1142 s += k; 1143 } 1144 } 1145 1146 } 1147 1148 static void create_beep(void) 1149 { 1150 Sint32 samples[12000]; /* approx. 0.14 seconds */ 1151 int i; 1152 1153 for (i = 0; i < 12000; ++i) { 1154 /* sawtooth wave at 3150 Hz, quadratic decay after 0.02 seconds. */ 1155 samples[i] = (i%28-14)*(i<2000 ? 2000 : (12000-i)*(12000-i)/50000); 1156 } 1157 beep = Mix_QuickLoad_RAW((Uint8*)samples, sizeof samples); 1158 } 1159 1160 static int initialize(void) 1161 { 1162 if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0) 1163 die("SDL Initialization Failure: %s", SDL_GetError()); 1164 atexit(SDL_Quit); 1165 screen = SDL_SetVideoMode(800, 600, 32, opt_fullscreen ? SDL_FULLSCREEN : SDL_SWSURFACE|SDL_DOUBLEBUF); 1166 if (!screen) 1167 die("SDL Video Initialization Failure: %s", SDL_GetError()); 1168 SDL_ShowCursor(SDL_DISABLE); 1169 IMG_Init(IMG_INIT_JPG|IMG_INIT_PNG); 1170 Mix_Init(MIX_INIT_OGG|MIX_INIT_MP3); 1171 if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048)<0) 1172 die("SDL Mixer Initialization Failure: %s", Mix_GetError()); 1173 SDL_WM_SetCaption(VERSION, 0); 1174 1175 fontdecompress(); 1176 fontprocess(1); 1177 fontprocess(2); 1178 fontprocess(3); 1179 read_keymap(); /* this is unfortunate, but get_sdlkey_from_name depends on SDL_Init. */ 1180 create_beep(); 1181 return 0; 1182 } 1183 1184 static SDL_Surface *stagefile_tmp; 1185 static int last_flip = -1; 1186 1187 static void callback_resource(const char *path) { 1188 /* try not to refresh the screen within 1/20 seconds */ 1189 int t = SDL_GetTicks(); 1190 if (last_flip >= 0 && (t - last_flip) < 50) return; 1191 1192 last_flip = t; 1193 SDL_BlitSurface(stagefile_tmp, newrect(0,0,800,20), screen, newrect(0,580,800,20)); 1194 printstr(screen, 797-8*strlen(path), 582, 1, path, 0x808080, 0xc0c0c0); 1195 SDL_Flip(screen); 1196 check_exit(); 1197 } 1198 1199 static void play_show_stagefile(void) 1200 { 1201 SDL_Surface *temp, *stagefile; 1202 char buf[256]; 1203 int i, j, t; 1204 1205 sprintf(buf, "%s: %s - %s", VERSION, metadata[2], metadata[0]); 1206 /*SDL_WM_SetCaption(buf, 0);*/ 1207 printstr(screen, 248, 284, 2, "loading bms file...", 0x202020, 0x808080); 1208 SDL_Flip(screen); 1209 1210 stagefile_tmp = newsurface(SDL_SWSURFACE, 800, 20); 1211 if (*metadata[3] && (temp = IMG_Load(adjust_path(metadata[3])))) { 1212 stagefile = SDL_DisplayFormat(temp); 1213 bicubic_interpolation(stagefile, screen); 1214 SDL_FreeSurface(temp); 1215 SDL_FreeSurface(stagefile); 1216 } 1217 if (opt_showinfo) { 1218 for (i = 0; i < 800; ++i) { 1219 for (j = 0; j < 42; ++j) putblendedpixel(screen, i, j, 0x101010, 64); 1220 for (j = 580; j < 600; ++j) putblendedpixel(screen, i, j, 0x101010, 64); 1221 } 1222 printstr(screen, 6, 4, 2, metadata[0], 0x808080, 0xffffff); 1223 printstr(screen, 792-8*strlen(metadata[1]), 4, 1, metadata[1], 0x808080, 0xffffff); 1224 printstr(screen, 792-8*strlen(metadata[2]), 20, 1, metadata[2], 0x808080, 0xffffff); 1225 i = sprintf(buf, "Level %d | BPM %.2f%s | %d note%s [%dKEY%s]", 1226 v_playlevel, bpm, "?"+((xflag&8)==0), xnnotes, 1227 "s"+(xnnotes==1), (xflag&1) ? 7 : 5, (xflag&2) ? "-LN" : ""); 1228 printstr(screen, 3, 582, 1, buf, 0x808080, 0xffffff); 1229 SDL_BlitSurface(screen, newrect(0,580,800,20), stagefile_tmp, newrect(0,0,800,20)); 1230 } 1231 SDL_Flip(screen); 1232 1233 t = SDL_GetTicks() + 3000; 1234 load_resource(opt_showinfo ? callback_resource : 0); 1235 if (opt_showinfo) { 1236 last_flip = -1; /* force update */ 1237 callback_resource("loading..."); 1238 SDL_FreeSurface(stagefile_tmp); 1239 } 1240 while ((int)SDL_GetTicks() < t) check_exit(); 1241 } 1242 1243 static void play_prepare(void) 1244 { 1245 int i, j, c; 1246 1247 /* panel position */ 1248 if ((xflag&4) == 0) { 1249 tkeyleft[6] = tkeyleft[15] = -1; 1250 tpanel1 -= tkeywidth[6] + 1; 1251 tpanel2 += tkeywidth[15] + 1; 1252 } 1253 if ((xflag&1) == 0) { 1254 tkeyleft[7] = tkeyleft[8] = tkeyleft[16] = tkeyleft[17] = -1; 1255 for (i = 9; i < 16; ++i) { 1256 if (i != 14 && tkeyleft[i] >= 0) 1257 tkeyleft[i] += tkeywidth[16] + tkeywidth[17] + 2; 1258 } 1259 if (tkeyleft[6] > 0) 1260 tkeyleft[6] -= tkeywidth[7] + tkeywidth[8] + 2; 1261 tpanel1 -= tkeywidth[7] + tkeywidth[8] + 2; 1262 tpanel2 += tkeywidth[16] + tkeywidth[17] + 2; 1263 } 1264 if (v_player == 1) { 1265 for (i = 9; i < 18; ++i) tkeyleft[i] = -1; 1266 } else if (v_player == 3) { 1267 for (i = 9; i < 18; ++i) tkeyleft[i] += tpanel1 - tpanel2; 1268 tpanel1 += 801 - tpanel2; 1269 } 1270 if (v_player % 2) { 1271 tpanel2 = 0; 1272 tbga = tpanel1 / 2 + 282; 1273 } 1274 1275 /* sprite */ 1276 sprite = newsurface(SDL_SWSURFACE, 1200, 600); 1277 for (i = 0; i < 18; ++i) { 1278 if (tkeyleft[i] < 0) continue; 1279 for (j = 140; j < 520; ++j) 1280 SDL_FillRect(sprite, newrect(tkeyleft[i],j,tkeywidth[i],1), blend(tkeycolor[i], 0, j-140, 1000)); 1281 if (i < 9) { 1282 for (j = 0; j*2 < tkeywidth[i]; ++j) 1283 SDL_FillRect(sprite, newrect(800+tkeyleft[i]+j,0,tkeywidth[i]-2*j,600), blend(tkeycolor[i], 0xffffff, tkeywidth[i]-j, tkeywidth[i])); 1284 } 1285 } 1286 for (j = -244; j < 556; ++j) { 1287 for (i = -10; i < 20; ++i) { 1288 c = (i*2+j*3+750) % 2000; 1289 c = blend(0xc0c0c0, 0x606060, c>1000 ? 1850-c : c-150, 700); 1290 putpixel(sprite, j+244, i+10, c); 1291 } 1292 for (i = -20; i < 60; ++i) { 1293 c = (i*3+j*2+750) % 2000; 1294 c = blend(0xc0c0c0, 0x404040, c>1000 ? 1850-c : c-150, 700); 1295 putpixel(sprite, j+244, i+540, c); 1296 } 1297 } 1298 SDL_FillRect(sprite, newrect(tpanel1+20,0,(tpanel2?tpanel2:820)-tpanel1-40,600), 0); 1299 for (i = 0; i < 20; ++i) { 1300 for (j = 20; j*j+i*i > 400; --j) { 1301 putpixel(sprite, tpanel1+j, i+10, 0); 1302 putpixel(sprite, tpanel1+j, 539-i, 0); 1303 if (tpanel2) { 1304 putpixel(sprite, tpanel2-j-1, i+10, 0); 1305 putpixel(sprite, tpanel2-j-1, 539-i, 0); 1306 } 1307 } 1308 } 1309 if (!tpanel2 && !opt_mode) { 1310 SDL_FillRect(sprite, newrect(0,584,368,16), 0x404040); 1311 SDL_FillRect(sprite, newrect(4,588,360,8), 0); 1312 } 1313 SDL_FillRect(sprite, newrect(10,564,tpanel1,1), 0x404040); 1314 1315 /* screen */ 1316 SDL_FillRect(screen, 0, map(0)); 1317 SDL_BlitSurface(sprite, newrect(0,0,800,30), screen, newrect(0,0,0,0)); 1318 SDL_BlitSurface(sprite, newrect(0,520,800,80), screen, newrect(0,520,0,0)); 1319 1320 /* video (if any) */ 1321 if (mpeg) { 1322 imgres[imgmpeg] = newsurface(SDL_SWSURFACE, 256, 256); 1323 SMPEG_enablevideo(mpeg, 1); 1324 SMPEG_setdisplay(mpeg, imgres[imgmpeg], NULL, NULL); 1325 } 1326 1327 /* configuration */ 1328 origintime = starttime = SDL_GetTicks(); 1329 targetspeed = playspeed; 1330 Mix_AllocateChannels(128); 1331 for (i = 0; i < 22; ++i) pcur[i] = 0; 1332 gradefactor = 1.5 - v_rank * .25; 1333 } 1334 1335 static double adjust_object_time(double base, double offset) 1336 { 1337 int i = (int)(base+1)-1; 1338 if ((i + 1 - base) * shorten[i] > offset) 1339 return base + offset / shorten[i]; 1340 offset -= (i + 1 - base) * shorten[i]; 1341 while (shorten[++i] <= offset) 1342 offset -= shorten[i]; 1343 return i + offset / shorten[i]; 1344 } 1345 1346 static double adjust_object_position(double base, double time) 1347 { 1348 int i = (int)(base+1)-1, j = (int)(time+1)-1; 1349 base = (time - j) * shorten[j] - (base - i) * shorten[i]; 1350 while (i < j) base += shorten[i++]; 1351 return base; 1352 } 1353 1354 static void update_grade(int grade) 1355 { 1356 ++scocnt[grade]; 1357 grademode = grade; 1358 gradetime = now + 700; 1359 1360 if (grade < 1) { 1361 scombo = 0; 1362 gauge -= 30; 1363 poorbga = now + 600; 1364 } else if (grade < 2) { 1365 scombo = 0; 1366 gauge -= 15; 1367 } else if (grade < 3) { 1368 /* do nothing */ 1369 } else { 1370 ++scombo; 1371 gauge += (grade<4 ? 2 : 3) + (scombo<100 ? scombo : 100) / 50; 1372 } 1373 1374 if (scombo > smaxcombo) smaxcombo = scombo; 1375 } 1376 1377 static int play_process(void) 1378 { 1379 int i, j, k, l, ibottom, eop; 1380 double bottom, top, line, tmp; 1381 char buf[99]; 1382 1383 if (adjustspeed) { 1384 tmp = targetspeed - playspeed; 1385 if (-0.001 < tmp && tmp < 0.001) { 1386 adjustspeed = 0; 1387 playspeed = targetspeed; 1388 for (i = 0; i < 22; ++i) prear[i] = pfront[i]; 1389 } else { 1390 playspeed += tmp * 0.1; 1391 } 1392 } 1393 1394 now = SDL_GetTicks(); 1395 if (now < stoptime) { 1396 bottom = startoffset; 1397 } else if (stoptime) { 1398 starttime = now; 1399 stoptime = 0; 1400 bottom = startoffset; 1401 } else { 1402 bottom = startoffset + (now - starttime) * bpm / startshorten / 24e4; 1403 } 1404 ibottom = (int)(bottom + 1) - 1; 1405 if (bottom > -1 && startshorten != shorten[ibottom]) { 1406 starttime += (int)((ibottom - startoffset) * 24e4 * startshorten / bpm); 1407 startoffset = ibottom; 1408 startshorten = shorten[ibottom]; 1409 } 1410 line = bottom;/*adjust_object_time(bottom, 0.03/playspeed);*/ 1411 top = adjust_object_time(bottom, 1.25/playspeed); 1412 eop = 1; 1413 for (i = 0; i < 22; ++i) { 1414 while (pfront[i] < nchannel[i] && channel[i][pfront[i]].time < bottom) ++pfront[i]; 1415 while (prear[i] < nchannel[i] && channel[i][prear[i]].time <= top) ++prear[i]; 1416 while (pcur[i] < nchannel[i] && channel[i][pcur[i]].time < line) { 1417 j = channel[i][pcur[i]].index; 1418 if (i == 18) { 1419 if (sndres[j]) { 1420 j = Mix_PlayChannel(-1, sndres[j], 0); 1421 if (j >= 0) { 1422 Mix_Volume(j, 96); 1423 Mix_GroupChannel(j, 1); 1424 } 1425 } 1426 } else if (i == 19) { 1427 bga[channel[i][pcur[i]].type] = j; 1428 bga_updated = 1; 1429 } else if (i == 20) { 1430 if (tmp = (channel[i][pcur[i]].type ? bpmtab[j] : j)) { 1431 starttime = now; 1432 startoffset = bottom; 1433 bpm = tmp; 1434 } 1435 } else if (i == 21) { 1436 if (now >= stoptime) stoptime = now; 1437 if (channel[i][pcur[i]].type) { 1438 stoptime += j; 1439 } else { 1440 stoptime += (int)(stoptab[j] * 1250 * startshorten / bpm); 1441 } 1442 startoffset = bottom; 1443 } else if (opt_mode && channel[i][pcur[i]].type != 3/*INVNOTE*/ && sndres[j]) { 1444 j = Mix_PlayChannel(-1, sndres[j], 0); 1445 if (j >= 0) Mix_GroupChannel(j, 0); 1446 score += (int)(300 * (1 + 1. * scombo / xnnotes)); 1447 update_grade(4); 1448 } 1449 ++pcur[i]; 1450 } 1451 if (i<18 && !opt_mode) { 1452 for (; pcheck[i] < pcur[i]; ++pcheck[i]) { 1453 j = channel[i][pcheck[i]].type; 1454 if (j < 0 || j == 3/*INVNOTE*/ || (j == 0/*LNDONE*/ && !thru[i])) continue; 1455 tmp = channel[i][pcheck[i]].time; 1456 tmp = (line - tmp) * shorten[(int)tmp] / bpm * gradefactor; 1457 if (tmp > 6e-4) update_grade(0); else break; 1458 } 1459 if (pfront[i] < nchannel[i]) eop = 0; 1460 } 1461 } 1462 1463 while (SDL_PollEvent(&event)) { 1464 if (event.type == SDL_QUIT) { 1465 return (eop ? 0 : -1); 1466 } else if (event.type == SDL_KEYUP) { 1467 if (event.key.keysym.sym == SDLK_ESCAPE) { 1468 return (eop ? 0 : -1); 1469 } else if (!opt_mode) { 1470 i = keymap[event.key.keysym.sym]; 1471 if (i >= 0 && i < 18 && tkeyleft[i] >= 0) { 1472 j = keypressed[i]; 1473 if (j > 0) --keypressed[i]; 1474 if (j == 1 && nchannel[i] && thru[i]) { 1475 for (j = pcur[i]; channel[i][j].type != 0/*LNDONE*/; --j); 1476 thru[i] = 0; 1477 tmp = (channel[i][j].time - line) * shorten[(int)line] / bpm * gradefactor; 1478 if (-6e-4 < tmp && tmp < 6e-4) { 1479 channel[i][j].type ^= -1; 1480 } else { 1481 update_grade(0); 1482 } 1483 } 1484 } 1485 } 1486 } else if (event.type == SDL_KEYDOWN) { 1487 i = keymap[event.key.keysym.sym]; 1488 if (i == 18/*SPEED_DOWN*/) { 1489 if (targetspeed > 20) targetspeed -= 5; 1490 else if (targetspeed > 10) targetspeed -= 1; 1491 else if (targetspeed > 1) targetspeed -= .5; 1492 else if (targetspeed > .201) targetspeed -= .2; 1493 else continue; 1494 adjustspeed = 1; 1495 Mix_PlayChannel(-1, beep, 0); 1496 } else if (i == 19/*SPEED_UP*/) { 1497 if (targetspeed < 1) targetspeed += .2; 1498 else if (targetspeed < 10) targetspeed += .5; 1499 else if (targetspeed < 20) targetspeed += 1; 1500 else if (targetspeed < 95) targetspeed += 5; 1501 else continue; 1502 adjustspeed = 1; 1503 Mix_PlayChannel(-1, beep, 0); 1504 } else if (!opt_mode) { 1505 if (i >= 0 && i < 18 && tkeyleft[i] >= 0) { 1506 if (keypressed[i]++ || !nchannel[i]) continue; 1507 1508 j = (pcur[i] < 1 || (pcur[i] < nchannel[i] && channel[i][pcur[i]-1].time + channel[i][pcur[i]].time < 2*line) ? pcur[i] : pcur[i]-1); 1509 if (sndres[channel[i][j].index]) { 1510 l = Mix_PlayChannel(-1, sndres[channel[i][j].index], 0); 1511 if (l >= 0) Mix_GroupChannel(l, 0); 1512 } 1513 1514 if (j < pcur[i]) { 1515 while (j >= 0 && channel[i][j].type == 3/*INVNOTE*/) --j; 1516 if (j < 0) continue; 1517 } else { 1518 while (j < nchannel[i] && channel[i][j].type == 3/*INVNOTE*/) ++j; 1519 if (j == nchannel[i]) continue; 1520 } 1521 1522 if (channel[i][j].type == 0/*LNDONE*/) { 1523 if (thru[i]) update_grade(0); 1524 } else if (channel[i][j].type >= 0) { 1525 tmp = (channel[i][j].time - line) * shorten[(int)line] / bpm * gradefactor; 1526 if (tmp < 0) tmp *= -1; 1527 if (channel[i][j].type >= 0 && tmp < 6e-4) { 1528 if (channel[i][j].type == 1/*LNSTART*/) thru[i] = 1; 1529 channel[i][j].type ^= -1; 1530 score += (int)((300 - tmp * 5e5) * (1 + 1. * scombo / xnnotes)); 1531 update_grade(tmp<0.6e-4 ? 4 : tmp<2.0e-4 ? 3 : tmp<3.5e-4 ? 2 : 1); 1532 } 1533 } 1534 } 1535 } 1536 } 1537 } 1538 if (bottom > length) { 1539 if (opt_mode ? Mix_Playing(-1)==0 : Mix_GroupNewer(1)==-1) return 0; 1540 } else if (bottom < -1) { 1541 return 0; 1542 } 1543 1544 SDL_FillRect(screen, newrect(0,30,tpanel1,490), map(0x404040)); 1545 if (tpanel2) SDL_FillRect(screen, newrect(tpanel2,30,800-tpanel2,490), map(0x404040)); 1546 for (i = 0; i < 18; ++i) { 1547 if (tkeyleft[i] < 0) continue; 1548 SDL_FillRect(screen, newrect(tkeyleft[i],30,tkeywidth[i],490), map(0)); 1549 if (keypressed[i]) { 1550 SDL_BlitSurface(sprite, newrect(tkeyleft[i],140,tkeywidth[i],380), screen, newrect(tkeyleft[i],140,0,0)); 1551 } 1552 } 1553 SDL_SetClipRect(screen, newrect(0,30,800,490)); 1554 for (i = 0; i < 18; ++i) { 1555 if (tkeyleft[i] < 0) continue; 1556 for (j = pfront[i]; j < prear[i]; ++j) { 1557 k = (int)(525 - 400 * playspeed * adjust_object_position(bottom, channel[i][j].time)); 1558 if (channel[i][j].type == 1/*LNSTART*/) { 1559 l = k + 5; 1560 k = (int)(530 - 400 * playspeed * adjust_object_position(bottom, channel[i][++j].time)); 1561 if (k < 30) k = 30; 1562 } else if (channel[i][j].type == 0/*LNDONE*/) { 1563 k += 5; 1564 l = 520; 1565 } else if (channel[i][j].type == 2/*NOTE*/) { 1566 l = k + 5; 1567 } else { 1568 continue; 1569 } 1570 if (k > 0 && l > k) { 1571 SDL_BlitSurface(sprite, newrect(800+tkeyleft[i%9],0,tkeywidth[i%9],l-k), screen, newrect(tkeyleft[i],k,0,0)); 1572 } 1573 } 1574 if (pfront[i]==prear[i] && prear[i]<nchannel[i] && channel[i][prear[i]].type==0/*LNDONE*/) { 1575 SDL_BlitSurface(sprite, newrect(800+tkeyleft[i%9],0,tkeywidth[i%9],490), screen, newrect(tkeyleft[i],30,0,0)); 1576 } 1577 } 1578 for (i = ibottom; i < top; ++i) { 1579 j = (int)(530 - 400 * playspeed * adjust_object_position(bottom, i)); 1580 SDL_FillRect(screen, newrect(0,j,tpanel1,1), map(0xc0c0c0)); 1581 if (tpanel2) SDL_FillRect(screen, newrect(tpanel2,j,800-tpanel2,1), map(0xc0c0c0)); 1582 } 1583 if (now < gradetime) { 1584 int delta = (gradetime - now - 400) / 30; 1585 if (delta < 0) delta = 0; 1586 printstr(screen, tpanel1/2-8*strlen(tgradestr[grademode]), 260 - delta, 2, 1587 tgradestr[grademode], tgradecolor[grademode][0], tgradecolor[grademode][1]); 1588 if (scombo > 1) { 1589 i = sprintf(buf, "%d COMBO", scombo); 1590 printstr(screen, tpanel1/2-4*i, 288 - delta, 1, buf, 0x808080, 0xffffff); 1591 } 1592 if (opt_mode) printstr(screen, tpanel1/2-24, 302 - delta, 1, "(AUTO)", 0x404040, 0xc0c0c0); 1593 if (!grademode) bga_updated = 1; 1594 } 1595 SDL_SetClipRect(screen, 0); 1596 if (bga_updated > 0 || (bga_updated < 0 && now >= poorbga) || (mpeg && SMPEG_status(mpeg) == SMPEG_PLAYING)) { 1597 SDL_FillRect(screen, newrect(tbga,172,256,256), map(0)); 1598 for (i = 0; i < 3; ++i) { 1599 if (bga_updated > 0 && bga[i] == imgmpeg && SMPEG_status(mpeg) != SMPEG_PLAYING) SMPEG_play(mpeg); 1600 } 1601 if (now < poorbga) { 1602 if (bga[2] >= 0 && imgres[bga[2]]) 1603 SDL_BlitSurface(imgres[bga[2]], newrect(0,0,256,256), screen, newrect(tbga,172,0,0)); 1604 bga_updated = -1; 1605 } else { 1606 for (i = 0; i < 2; ++i) 1607 if (bga[i] >= 0 && imgres[bga[i]]) 1608 SDL_BlitSurface(imgres[bga[i]], newrect(0,0,256,256), screen, newrect(tbga,172,0,0)); 1609 bga_updated = 0; 1610 } 1611 } 1612 1613 i = (now - origintime) / 1000; 1614 j = xduration / 1000; 1615 sprintf(buf, "SCORE %07d%c%4.1fx%c%02d:%02d / %02d:%02d%c@%9.4f%cBPM %6.2f", 1616 score, 0, targetspeed, 0, i/60, i%60, j/60, j%60, 0, bottom, 0, bpm); 1617 SDL_BlitSurface(sprite, newrect(0,0,800,30), screen, newrect(0,0,0,0)); 1618 SDL_BlitSurface(sprite, newrect(0,520,800,80), screen, newrect(0,520,0,0)); 1619 printstr(screen, 10, 8, 1, buf, 0, 0); 1620 printstr(screen, 5, 522, 2, buf+14, 0, 0); 1621 printstr(screen, tpanel1-94, 565, 1, buf+20, 0, 0x404040); 1622 printstr(screen, 95, 538, 1, buf+34, 0, 0); 1623 printstr(screen, 95, 522, 1, buf+45, 0, 0); 1624 i = (now - origintime) * tpanel1 / xduration; 1625 printchar(screen, 6+(i<tpanel1?i:tpanel1), 548, 1, -1, 0x404040, 0x404040); 1626 if (!tpanel2 && !opt_mode) { 1627 if (gauge > 512) gauge = 512; 1628 k = (int)(160*startshorten*(1+bottom)) % 40; /* i.e. cycles four times per measure */ 1629 i = (gauge<0 ? 0 : (gauge*400>>9) - k); 1630 j = (gauge>=survival ? 0xc0 : 0xc0 - k*4) << 16; 1631 SDL_FillRect(screen, newrect(4,588,i>360?360:i<5?5:i,8), map(j)); 1632 } 1633 1634 SDL_Flip(screen); 1635 return 1; 1636 } 1637 1638 /******************************************************************************/ 1639 /* entry point */ 1640 1641 static int play(void) 1642 { 1643 int i; 1644 char *pos1, *pos2; 1645 1646 if (initialize()) return 1; 1647 1648 if (read_bms()) die("Couldn't load BMS file: %s", bmspath); 1649 if (v_player == 4) clone_bms(); 1650 if (opt_random) { 1651 if (v_player == 3) { 1652 shuffle_bms(opt_random, 0); 1653 } else { 1654 shuffle_bms(opt_random, 1); 1655 if (v_player != 1) shuffle_bms(opt_random, 2); 1656 } 1657 } 1658 get_bms_info(); 1659 1660 /* get basename of bmspath */ 1661 pos1 = strrchr(bmspath, '/'); 1662 pos2 = strrchr(bmspath, '\\'); 1663 if (!pos1) pos1 = bmspath + 1; 1664 if (!pos2) pos2 = bmspath + 1; 1665 (pos1>pos2 ? pos1 : pos2)[1] = '\0'; 1666 if (pos1 == pos2) { /* will be pos1 = pos2 = bmspath + 1 */ 1667 bmspath[0] = '.'; 1668 bmspath[1] = sep; 1669 } 1670 1671 dirinit(); 1672 play_show_stagefile(); 1673 dirfinal(); 1674 1675 xduration = get_bms_duration(); 1676 play_prepare(); 1677 while ((i = play_process()) > 0); 1678 1679 if (!opt_mode && i == 0) { 1680 if (gauge >= survival) { 1681 printf("*** CLEARED! ***\n"); 1682 for (i = 4; i >= 0; --i) 1683 printf("%-5s %4d %s", tgradestr[i], scocnt[i], "\n"+(i!=2)); 1684 printf("MAX COMBO %d\nSCORE %07d (max %07d)\n", smaxcombo, score, xscore); 1685 } else { 1686 printf("YOU FAILED!\n"); 1687 } 1688 } 1689 1690 return 0; 1691 } 1692 1693 int usage(void) 1694 { 1695 fprintf(stderr, 1696 "%s -- the simple BMS player\n" 1697 "http://mearie.org/projects/angolmois/\n\n" 1698 "Usage: %s <options> <path>\n" 1699 " Accepts any BMS, BME or BML file.\n" 1700 " Resources should be in the same directory as the BMS file.\n\n" 1701 "Options:\n" 1702 " -h, --help This help\n" 1703 " -V, --version Shows the version\n" 1704 " -a #.#, --speed #.# Sets the initial play speed (default: 1.0x)\n" 1705 " -# Same as '-a #.0'\n" 1706 " -v, --autoplay Enables AUTO PLAY (viewer) mode\n" 1707 " --fullscreen Enables the fullscreen mode (default)\n" 1708 " -w, --no-fullscreen Disables the fullscreen mode\n" 1709 " --info Shows a brief information about the song (default)\n" 1710 " -q, --no-info Do not show an information about the song\n" 1711 " -m, --mirror Uses a mirror modifier\n" 1712 " -r, --random Uses a random modifier\n" 1713 " -R, --random-ex Uses a random modifier, even for scratches\n" 1714 " -s, --srandom Uses a super-random modifier\n" 1715 " -S, --srandom-ex Uses a super-random modifier, even for scratches\n" 1716 " --bga Loads and shows the BGA (default)\n" 1717 " -B, --no-bga Do not load and show the BGA\n" 1718 " -M, --no-movie Do not load and show the BGA movie\n\n" 1719 "Environment Variables:\n" 1720 " ANGOLMOIS_1P_KEYS=<1>|<2>|<3>|<4>|<5>|<scratch>|<pedal>|<6>|<7>\n" 1721 " ANGOLMOIS_2P_KEYS=<1>|<2>|<3>|<4>|<5>|<scratch>|<pedal>|<6>|<7>\n" 1722 " ANGOLMOIS_SPEED_KEYS=<speed down>|<speed up>\n" 1723 " Specifies the keys used for gameplay. Key names should follow them of\n" 1724 " SDL (e.g. 'a', 'right shift' etc.). The mapping for 1P/2P is as follows:\n" 1725 " <2> <4> <6>\n" 1726 " <scratch> <1> <3> <5> <7> <pedal>\n" 1727 " One can map multiple keys by separating key names with '%%'.\n" 1728 " The default mapping is to use zsxdcfv and mk,l.;/ for 1P and 2P,\n" 1729 " respectively. F3 and F4 is used for speed adjustment.\n\n", 1730 VERSION, argv0); 1731 return 1; 1732 } 1733 1734 int main(int argc, char **argv) 1735 { 1736 static char *longargs[] = 1737 {"h--help", "V--version", "a--speed", "v--autoplay", "w--windowed", 1738 "w--no-fullscreen", " --fullscreen", " --info", "q--no-info", "m--mirror", 1739 "r--random", "R--random-ex", "s--srandom", "S--srandom-ex", " --bga", 1740 "B--no-bga", " --movie", "M--no-movie", NULL}; 1741 char buf[512]={0}; 1742 int i, j, cont; 1743 1744 argv0 = argv[0]; 1745 for (i = 1; argv[i]; ++i) { 1746 if (argv[i][0] != '-') { 1747 if (!bmspath) bmspath = argv[i]; 1748 } else if (!strcmp(argv[i], "--")) { 1749 if (!bmspath) bmspath = argv[++i]; 1750 break; 1751 } else { 1752 if (argv[i][1] == '-') { 1753 for (j = 0; longargs[j]; ++j) { 1754 if (!strcmp(argv[i], longargs[j]+1)) { 1755 argv[i][1] = longargs[j][0]; 1756 argv[i][2] = '\0'; 1757 break; 1758 } 1759 } 1760 if (!longargs[j]) die("Invalid option: %s", argv[i]); 1761 } 1762 for (j = cont = 1; cont; ++j) { 1763 switch (argv[i][j]) { 1764 case 'h': case 'V': usage(); exit(1); 1765 case 'v': opt_mode = 1; break; 1766 case 'w': opt_fullscreen = 0; break; 1767 case 'q': opt_showinfo = 0; break; 1768 case 'm': opt_random = 1; break; 1769 case 's': opt_random = 2; break; 1770 case 'S': opt_random = 3; break; 1771 case 'r': opt_random = 4; break; 1772 case 'R': opt_random = 5; break; 1773 case 'a': 1774 playspeed = atof(argv[i][++j] ? argv[i]+j : argv[++i]); 1775 if (playspeed <= 0) playspeed = 1; 1776 if (playspeed < .1) playspeed = .1; 1777 if (playspeed > 99) playspeed = 99; 1778 case 'B': opt_bga = 2; break; 1779 case 'M': opt_bga = 1; break; 1780 case '\0': cont = 0; 1781 case ' ': break; 1782 default: 1783 if (argv[i][j] >= '1' && argv[i][j] <= '9') { 1784 playspeed = argv[i][j] - '0'; 1785 } else { 1786 die("Invalid option: -%c", argv[i][j]); 1787 } 1788 } 1789 } 1790 } 1791 } 1792 1793 if (!bmspath && filedialog(buf)) bmspath = buf; 1794 return bmspath ? play() : usage(); 1795 } 1796 1797 /* vim: set ts=4 sw=4: */