Changeset 87:4cf3f3a15bec

angolmois

File angolmois.c

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

Powered by Mercurial