Changeset 85:75cca35e667a

angolmois

File angolmois.c

← 84:1c54d34cca9a 85:75cca35e667a 87:4cf3f3a15bec
Author:
Kang Seonghoon <public+hg@mearie.org>
Committed on
Permission:
-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: */

Powered by Mercurial