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