ft2-clone

Fasttracker 2 clone
Log | Files | Refs | README | LICENSE

ft2_module_loader.c (18766B)


      1 // for finding memory leaks in debug mode with Visual Studio
      2 #if defined _DEBUG && defined _MSC_VER
      3 #include <crtdbg.h>
      4 #endif
      5 
      6 #include <stdio.h>
      7 #include <stdint.h>
      8 #include <stdbool.h>
      9 #ifndef _WIN32
     10 #include <unistd.h>
     11 #endif
     12 #include "ft2_header.h"
     13 #include "scopes/ft2_scopes.h"
     14 #include "ft2_trim.h"
     15 #include "ft2_inst_ed.h"
     16 #include "ft2_sample_ed.h"
     17 #include "ft2_wav_renderer.h"
     18 #include "ft2_pattern_ed.h"
     19 #include "ft2_gui.h"
     20 #include "ft2_diskop.h"
     21 #include "ft2_sample_loader.h"
     22 #include "ft2_smpfx.h"
     23 #include "ft2_mouse.h"
     24 #include "ft2_midi.h"
     25 #include "ft2_events.h"
     26 #include "ft2_video.h"
     27 #include "ft2_structs.h"
     28 #include "ft2_sysreqs.h"
     29 
     30 bool detectBEM(FILE *f);
     31 bool loadBEM(FILE *f, uint32_t filesize);
     32 
     33 bool loadIT(FILE *f, uint32_t filesize);
     34 bool loadDIGI(FILE *f, uint32_t filesize);
     35 bool loadMOD(FILE *f, uint32_t filesize);
     36 bool loadS3M(FILE *f, uint32_t filesize);
     37 bool loadSTK(FILE *f, uint32_t filesize);
     38 bool loadSTM(FILE *f, uint32_t filesize);
     39 bool loadXM(FILE *f, uint32_t filesize);
     40 
     41 enum
     42 {
     43 	FORMAT_UNKNOWN = 0,
     44 	FORMAT_POSSIBLY_STK = 1,
     45 	FORMAT_XM = 2,
     46 	FORMAT_MOD = 3,
     47 	FORMAT_S3M = 4,
     48 	FORMAT_STM = 5,
     49 	FORMAT_DIGI = 6,
     50 	FORMAT_BEM = 7,
     51 	FORMAT_IT = 8
     52 };
     53 
     54 // file extensions accepted by Disk Op. in module mode
     55 char *supportedModExtensions[] =
     56 {
     57 	"xm", "ft", "nst", "stk", "mod", "s3m", "stm", "fst",
     58 	"digi", "bem", "it",
     59 
     60 	// IMPORTANT: Remember comma after last entry above
     61 	"END_OF_LIST" // do NOT move, remove or edit this line!
     62 };
     63 
     64 // globals for module loaders
     65 volatile bool tmpLinearPeriodsFlag;
     66 int16_t patternNumRowsTmp[MAX_PATTERNS];
     67 note_t *patternTmp[MAX_PATTERNS];
     68 instr_t *instrTmp[1+256];
     69 song_t songTmp;
     70 // --------------------------
     71 
     72 static volatile bool musicIsLoading, moduleLoaded, moduleFailedToLoad;
     73 static SDL_Thread *thread;
     74 static uint8_t oldPlayMode;
     75 static void setupLoadedModule(void);
     76 static void freeTmpModule(void);
     77 
     78 // Crude module detection routine. These aren't always accurate detections!
     79 static int8_t detectModule(FILE *f)
     80 {
     81 	uint8_t D[256], I[4];
     82 
     83 	fseek(f, 0, SEEK_END);
     84 	uint32_t fileLength = (uint32_t)ftell(f);
     85 	rewind(f);
     86 
     87 	memset(D, 0, sizeof (D));
     88 	fread(D, 1, sizeof (D), f);
     89 	fseek(f, 1080, SEEK_SET); // MOD ID
     90 	I[0] = I[1] = I[2] = I[3] = 0;
     91 	fread(I, 1, 4, f);
     92 	rewind(f);
     93 
     94 	// BEM ("UN05", from XM only, MikMod)
     95 	if (detectBEM(f))
     96 		return FORMAT_BEM;
     97 
     98 	// DIGI Booster (non-Pro)
     99 	if (!memcmp("DIGI Booster module", &D[0x00], 19+1) && D[0x19] >= 1 && D[0x19] <= 8)
    100 		return FORMAT_DIGI;
    101 
    102 	// Scream Tracker 3 S3M (and compatible trackers)
    103 	if (!memcmp("SCRM", &D[0x2C], 4) && D[0x1D] == 16) // XXX: byte=16 in all cases?
    104 		return FORMAT_S3M;
    105 
    106 	// Scream Tracker 2 STM
    107 	if ((!memcmp("!Scream!", &D[0x14], 8) || !memcmp("BMOD2STM", &D[0x14], 8) ||
    108 		 !memcmp("WUZAMOD!", &D[0x14], 8) || !memcmp("SWavePro", &D[0x14], 8)) && D[0x1D] == 2) // XXX: byte=2 for "WUZAMOD!"/"SWavePro" ?
    109 	{
    110 		return FORMAT_STM;
    111 	}
    112 
    113 	// Generic multi-channel MOD (1..9 channels)
    114 	if (isdigit(I[0]) && I[0] != '0' && I[1] == 'C' && I[2] == 'H' && I[3] == 'N') // xCHN
    115 		return FORMAT_MOD;
    116 
    117 	// Digital Tracker (Atari Falcon)
    118 	if (I[0] == 'F' && I[1] == 'A' && I[2] == '0' && I[3] >= '4' && I[3] <= '8') // FA0x (x=4..8)
    119 		return FORMAT_MOD;
    120 
    121 	// Generic multi-channel MOD (10..99 channels)
    122 	if (isdigit(I[0]) && isdigit(I[1]) && I[0] != '0' && I[2] == 'C' && I[3] == 'H') // xxCH
    123 		return FORMAT_MOD;
    124 
    125 	// Generic multi-channel MOD (10..99 channels)
    126 	if (isdigit(I[0]) && isdigit(I[1]) && I[0] != '0' && I[2] == 'C' && I[3] == 'N') // xxCN (same as xxCH)
    127 		return FORMAT_MOD;
    128 	
    129 	// ProTracker and generic MOD formats
    130 	if (!memcmp("M.K.", I, 4) || !memcmp("M!K!", I, 4) || !memcmp("NSMS", I, 4) ||
    131 		!memcmp("LARD", I, 4) || !memcmp("PATT", I, 4) || !memcmp("FLT4", I, 4) ||
    132 		!memcmp("FLT8", I, 4) || !memcmp("EXO4", I, 4) || !memcmp("EXO8", I, 4) ||
    133 		!memcmp("N.T.", I, 4) || !memcmp("M&K!", I, 4) || !memcmp("FEST", I, 4) ||
    134 		!memcmp("CD61", I, 4) || !memcmp("CD81", I, 4) || !memcmp("OKTA", I, 4) ||
    135 		!memcmp("OCTA", I, 4))
    136 	{
    137 		return FORMAT_MOD;
    138 	}
    139 
    140 	// Impulse Tracker and compatible trackers
    141 	if (!memcmp("IMPM", D, 4) && D[0x1D] == 0)
    142 		return FORMAT_IT;
    143 
    144 	/* Fasttracker II XM and compatible trackers.
    145 	** Note: This test can falsely be true for STK modules (and non-supported files) where the
    146 	** first 17 bytes start with "Extended Module: ". This is unlikely to happen.
    147 	*/
    148 	if (!memcmp("Extended Module: ", &D[0x00], 17))
    149 		return FORMAT_XM;
    150 
    151 	/* Lastly, we assume that the file is either a 15-sample STK or an unsupported file.
    152 	** Let's assume it's an STK and do some sanity checks. If they fail, we have an
    153 	** unsupported file.
    154 	*/
    155 
    156 	// minimum and maximum (?) possible size for a supported STK
    157 	if (fileLength < 1624 || fileLength > 984634)
    158 		return FORMAT_UNKNOWN;
    159 
    160 	// test STK numOrders+BPM for illegal values
    161 	fseek(f, 470, SEEK_SET);
    162 	D[0] = D[1] = 0;
    163 	fread(D, 1, 2, f);
    164 	rewind(f);
    165 
    166 	if (D[0] <= 128 && D[1] <= 220)
    167 		return FORMAT_POSSIBLY_STK;
    168 
    169 	return FORMAT_UNKNOWN;
    170 }
    171 
    172 static bool doLoadMusic(bool externalThreadFlag)
    173 {
    174 	// setup message box functions
    175 	loaderMsgBox = externalThreadFlag ? myLoaderMsgBoxThreadSafe : myLoaderMsgBox;
    176 	loaderSysReq = externalThreadFlag ? okBoxThreadSafe : okBox;
    177 
    178 	if (editor.tmpFilenameU == NULL)
    179 	{
    180 		loaderMsgBox("Generic memory fault during loading!");
    181 		goto loadError;
    182 	}
    183 
    184 	FILE *f = UNICHAR_FOPEN(editor.tmpFilenameU, "rb");
    185 	if (f == NULL)
    186 	{
    187 		loaderMsgBox("General I/O error during loading! Is the file in use? Does it exist?");
    188 		goto loadError;
    189 	}
    190 
    191 	int8_t format = detectModule(f);
    192 	fseek(f, 0, SEEK_END);
    193 	uint32_t filesize = ftell(f);
    194 
    195 	rewind(f);
    196 	switch (format)
    197 	{
    198 		case FORMAT_XM: moduleLoaded = loadXM(f, filesize); break;
    199 		case FORMAT_S3M: moduleLoaded = loadS3M(f, filesize); break;
    200 		case FORMAT_STM: moduleLoaded = loadSTM(f, filesize); break;
    201 		case FORMAT_MOD: moduleLoaded = loadMOD(f, filesize); break;
    202 		case FORMAT_POSSIBLY_STK: moduleLoaded = loadSTK(f, filesize); break;
    203 		case FORMAT_DIGI: moduleLoaded = loadDIGI(f, filesize); break;
    204 		case FORMAT_BEM: moduleLoaded = loadBEM(f, filesize); break;
    205 		case FORMAT_IT: moduleLoaded = loadIT(f, filesize); break;
    206 
    207 		default:
    208 			loaderMsgBox("This file is not a supported module!");
    209 		break;
    210 	}
    211 	fclose(f);
    212 
    213 	if (!moduleLoaded)
    214 		goto loadError;
    215 
    216 	moduleLoaded = true;
    217 	return true;
    218 
    219 loadError:
    220 	freeTmpModule();
    221 	moduleFailedToLoad = true;
    222 	return false;
    223 }
    224 
    225 static void clearTmpModule(void)
    226 {
    227 	memset(patternTmp, 0, sizeof (patternTmp));
    228 	memset(instrTmp, 0, sizeof (instrTmp));
    229 	memset(&songTmp, 0, sizeof (songTmp));
    230 
    231 	for (uint32_t i = 0; i < MAX_PATTERNS; i++)
    232 		patternNumRowsTmp[i] = 64;
    233 }
    234 
    235 static int32_t SDLCALL loadMusicThread(void *ptr)
    236 {
    237 	return doLoadMusic(true);
    238 	(void)ptr;
    239 }
    240 
    241 void loadMusic(UNICHAR *filenameU)
    242 {
    243 	if (musicIsLoading || filenameU == NULL)
    244 		return;
    245 
    246 	mouseAnimOn();
    247 
    248 	musicIsLoading = true;
    249 	moduleLoaded = false;
    250 	moduleFailedToLoad = false;
    251 
    252 	clearTmpModule(); // clear stuff from last loading session (very important)
    253 	UNICHAR_STRCPY(editor.tmpFilenameU, filenameU);
    254 
    255 	thread = SDL_CreateThread(loadMusicThread, NULL, NULL);
    256 	if (thread == NULL)
    257 	{
    258 		editor.loadMusicEvent = EVENT_NONE;
    259 		okBox(0, "System message", "Couldn't create thread!", NULL);
    260 		musicIsLoading = false;
    261 		return;
    262 	}
    263 
    264 	SDL_DetachThread(thread);
    265 }
    266 
    267 bool loadMusicUnthreaded(UNICHAR *filenameU, bool autoPlay)
    268 {
    269 	if (filenameU == NULL)
    270 		return false;
    271 
    272 	clearTmpModule(); // clear stuff from last loading session (very important)
    273 	UNICHAR_STRCPY(editor.tmpFilenameU, filenameU);
    274 
    275 	editor.loadMusicEvent = EVENT_NONE;
    276 	doLoadMusic(false);
    277 
    278 	if (moduleLoaded)
    279 	{
    280 		setupLoadedModule();
    281 		if (autoPlay)
    282 			startPlaying(PLAYMODE_SONG, 0);
    283 
    284 		return true;
    285 	}
    286 
    287 	return false;
    288 }
    289 
    290 bool allocateTmpPatt(int32_t pattNum, uint16_t numRows)
    291 {
    292 	patternTmp[pattNum] = (note_t *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1);
    293 	if (patternTmp[pattNum] == NULL)
    294 		return false;
    295 
    296 	patternNumRowsTmp[pattNum] = numRows;
    297 	return true;
    298 }
    299 
    300 bool allocateTmpInstr(int16_t insNum)
    301 {
    302 	if (instrTmp[insNum] != NULL)
    303 		return false; // already allocated
    304 
    305 	instr_t *ins = (instr_t *)calloc(1, sizeof (instr_t));
    306 	if (ins == NULL)
    307 		return false;
    308 
    309 	sample_t *s = ins->smp;
    310 	for (int32_t i = 0; i < MAX_SMP_PER_INST; i++, s++)
    311 	{
    312 		s->panning = 128;
    313 		s->volume = 64;
    314 	}
    315 
    316 	instrTmp[insNum] = ins;
    317 	return true;
    318 }
    319 
    320 static void freeTmpModule(void) // called on module load error
    321 {
    322 	// free all patterns
    323 	for (int32_t i = 0; i < MAX_PATTERNS; i++)
    324 	{
    325 		if (patternTmp[i] != NULL)
    326 		{
    327 			free(patternTmp[i]);
    328 			patternTmp[i] = NULL;
    329 		}
    330 	}
    331 
    332 	// free all instruments and samples
    333 	for (int32_t i = 1; i <= 256; i++) // if >128 instruments, we fake-load up to 128 extra (and discard them later)
    334 	{
    335 		if (instrTmp[i] == NULL)
    336 			continue;
    337 
    338 		sample_t *s = instrTmp[i]->smp;
    339 		for (int32_t j = 0; j < MAX_SMP_PER_INST; j++, s++)
    340 			freeSmpData(s);
    341 
    342 		free(instrTmp[i]);
    343 		instrTmp[i] = NULL;
    344 	}
    345 }
    346 
    347 bool tmpPatternEmpty(uint16_t pattNum)
    348 {
    349 	if (patternTmp[pattNum] == NULL)
    350 		return true;
    351 
    352 	uint8_t *scanPtr = (uint8_t *)patternTmp[pattNum];
    353 	const uint32_t scanLen = patternNumRowsTmp[pattNum] * TRACK_WIDTH;
    354 
    355 	for (uint32_t i = 0; i < scanLen; i++)
    356 	{
    357 		if (scanPtr[i] != 0)
    358 			return false;
    359 	}
    360 
    361 	return true;
    362 }
    363 
    364 void clearUnusedChannels(note_t *pattPtr, int16_t numRows, int32_t numChannels)
    365 {
    366 	if (pattPtr == NULL || numChannels >= MAX_CHANNELS)
    367 		return;
    368 
    369 	const int32_t width = sizeof (note_t) * (MAX_CHANNELS - numChannels);
    370 
    371 	note_t *p = &pattPtr[numChannels];
    372 	for (int32_t i = 0; i < numRows; i++, p += MAX_CHANNELS)
    373 		memset(p, 0, width);
    374 }
    375 
    376 // called from input/video thread after the module was done loading
    377 static void setupLoadedModule(void)
    378 {
    379 	lockMixerCallback();
    380 
    381 	freeAllInstr();
    382 	freeAllPatterns();
    383 
    384 	oldPlayMode = playMode;
    385 	playMode = PLAYMODE_IDLE;
    386 	songPlaying = false;
    387 
    388 #ifdef HAS_MIDI
    389 	midi.currMIDIVibDepth = 0;
    390 	midi.currMIDIPitch = 0;
    391 #endif
    392 
    393 	memset(editor.keyOnTab, 0, sizeof (editor.keyOnTab));
    394 
    395 	// copy over new pattern pointers and lengths
    396 	for (int32_t i = 0; i < MAX_PATTERNS; i++)
    397 	{
    398 		pattern[i] = patternTmp[i];
    399 		patternNumRows[i] = patternNumRowsTmp[i];
    400 	}
    401 
    402 	// copy over song struct
    403 	memcpy(&song, &songTmp, sizeof (song_t));
    404 	fixSongName();
    405 
    406 	// copy over new instruments (includes sample pointers)
    407 	for (int16_t i = 1; i <= MAX_INST; i++)
    408 	{
    409 		instr[i] = instrTmp[i];
    410 		fixInstrAndSampleNames(i);
    411 
    412 		if (instr[i] != NULL)
    413 		{
    414 			sanitizeInstrument(instr[i]);
    415 			for (int32_t j = 0; j < MAX_SMP_PER_INST; j++)
    416 			{
    417 				sample_t *s = &instr[i]->smp[j];
    418 
    419 				sanitizeSample(s);
    420 				if (s->dataPtr != NULL)
    421 					fixSample(s); // prepare sample for branchless linear interpolation
    422 			}
    423 		}
    424 	}
    425 
    426 	// we are the owners of the allocated memory ptrs set by the loader thread now
    427 
    428 	// support non-even channel numbers
    429 	if (song.numChannels & 1)
    430 	{
    431 		song.numChannels++;
    432 		if (song.numChannels > MAX_CHANNELS)
    433 			song.numChannels = MAX_CHANNELS;
    434 	}
    435 
    436 	song.numChannels = CLAMP(song.numChannels, 2, MAX_CHANNELS);
    437 	song.songLength = CLAMP(song.songLength, 1, MAX_ORDERS);
    438 	song.BPM = CLAMP(song.BPM, MIN_BPM, MAX_BPM);
    439 	song.initialSpeed = song.speed = CLAMP(song.speed, 1, MAX_SPEED);
    440 
    441 	if (song.songLoopStart >= song.songLength)
    442 		song.songLoopStart = 0;
    443 
    444 	song.globalVolume = 64;
    445 
    446 	// remove overflown stuff in pattern data (FT2 doesn't do this)
    447 	for (int32_t i = 0; i < MAX_PATTERNS; i++)
    448 	{
    449 		if (patternNumRows[i] <= 0)
    450 			patternNumRows[i] = 64;
    451 
    452 		if (patternNumRows[i] > MAX_PATT_LEN)
    453 			patternNumRows[i] = MAX_PATT_LEN;
    454 
    455 		if (pattern[i] == NULL)
    456 			continue;
    457 
    458 		note_t *p = pattern[i];
    459 		for (int32_t j = 0; j < MAX_PATT_LEN * MAX_CHANNELS; j++, p++)
    460 		{
    461 			if (p->note > 97)
    462 				p->note = 0;
    463 
    464 			if (p->instr > 128)
    465 				p->instr = 0;
    466 
    467 			if (p->efx > 35)
    468 			{
    469 				p->efx = 0;
    470 				p->efxData = 0;
    471 			}
    472 		}
    473 	}
    474 
    475 	setScrollBarEnd(SB_POS_ED, (song.songLength - 1) + 5);
    476 	setScrollBarPos(SB_POS_ED, 0, false);
    477 
    478 	resetChannels();
    479 	setPos(0, 0, true);
    480 	setMixerBPM(song.BPM);
    481 
    482 	editor.tmpPattern = editor.editPattern; // set kludge variable
    483 	editor.BPM = song.BPM;
    484 	editor.speed = song.speed;
    485 	editor.tick = song.tick;
    486 	editor.globalVolume = song.globalVolume;
    487 
    488 	setLinearPeriods(tmpLinearPeriodsFlag);
    489 
    490 	unlockMixerCallback();
    491 
    492 	editor.currVolEnvPoint = 0;
    493 	editor.currPanEnvPoint = 0;
    494 
    495 	refreshScopes();
    496 	exitTextEditing();
    497 	updateTextBoxPointers();
    498 	resetChannelOffset();
    499 	updateChanNums();
    500 	resetWavRenderer();
    501 	clearPattMark();
    502 	clearSampleUndo();
    503 	resetTrimSizes();
    504 	resetPlaybackTime();
    505 
    506 	diskOpSetFilename(DISKOP_ITEM_MODULE, editor.tmpFilenameU);
    507 
    508 	// redraw top part of screen
    509 	if (ui.extendedPatternEditor)
    510 	{
    511 		togglePatternEditorExtended(); // exit
    512 		togglePatternEditorExtended(); // re-enter (force redrawing)
    513 	}
    514 	else
    515 	{
    516 		// redraw top screen
    517 		hideTopScreen();
    518 		showTopScreen(true);
    519 	}
    520 
    521 	updateSampleEditorSample();
    522 	showBottomScreen(); // redraw bottom screen (also redraws pattern editor)
    523 
    524 	if (ui.instEditorShown)
    525 		drawPiano(NULL); // redraw piano now (since if playing = wait for next tick update)
    526 
    527 	removeSongModifiedFlag();
    528 
    529 	moduleFailedToLoad = false;
    530 	moduleLoaded = false;
    531 	editor.loadMusicEvent = EVENT_NONE;
    532 }
    533 
    534 bool handleModuleLoadFromArg(int argc, char **argv)
    535 {
    536 	// we always expect only one parameter, and that it is the module
    537 
    538 	if (argc != 2 || argv[1] == NULL || argv[1][0] == '\0')
    539 		return false;
    540 
    541 #ifdef __APPLE__
    542 	if (argc == 2 && !strncmp(argv[1], "-psn_", 5))
    543 		return false; // OS X < 10.9 passes a -psn_x_xxxxx parameter on double-click launch
    544 #endif
    545 
    546 	const uint32_t filenameLen = (const uint32_t)strlen(argv[1]);
    547 
    548 	UNICHAR *tmpPathU = (UNICHAR *)malloc((PATH_MAX + 1) * sizeof (UNICHAR));
    549 	if (tmpPathU == NULL)
    550 	{
    551 		okBox(0, "System message", "Not enough memory!", NULL);
    552 		return false;
    553 	}
    554 
    555 	UNICHAR *filenameU = (UNICHAR *)malloc((filenameLen + 1) * sizeof (UNICHAR));
    556 	if (filenameU == NULL)
    557 	{
    558 		free(tmpPathU);
    559 		okBox(0, "System message", "Not enough memory!", NULL);
    560 		return false;
    561 	}
    562 
    563 	tmpPathU[0] = 0;
    564 	filenameU[0] = 0;
    565 
    566 #ifdef _WIN32
    567 	MultiByteToWideChar(CP_UTF8, 0, argv[1], -1, filenameU, filenameLen+1);
    568 #else
    569 	strcpy(filenameU, argv[1]);
    570 #endif
    571 
    572 	// store old path
    573 	UNICHAR_GETCWD(tmpPathU, PATH_MAX);
    574 
    575 	// set path to where the main executable is
    576 	UNICHAR_CHDIR(editor.binaryPathU);
    577 
    578 	const int32_t filesize = getFileSize(filenameU);
    579 	if (filesize == -1 || filesize >= 512L*1024*1024) // 1) >=2GB   2) >=512MB
    580 	{
    581 		free(filenameU);
    582 		UNICHAR_CHDIR(tmpPathU); // set old path back
    583 		free(tmpPathU);
    584 
    585 		okBox(0, "System message", "Error: The module is too big to be loaded!", NULL);
    586 		return false;
    587 	}
    588 
    589 	bool result = loadMusicUnthreaded(filenameU, true);
    590 
    591 	free(filenameU);
    592 	UNICHAR_CHDIR(tmpPathU); // set old path back
    593 	free(tmpPathU);
    594 
    595 	return result;
    596 }
    597 
    598 static bool fileIsModule(UNICHAR *pathU)
    599 {
    600 	FILE *f = UNICHAR_FOPEN(pathU, "rb");
    601 	if (f == NULL)
    602 		return false;
    603 
    604 	int8_t modFormat = detectModule(f);
    605 	fclose(f);
    606 
    607 	/* If the module was not identified (possibly STK type),
    608 	** check the file extension and handle it as a module only
    609 	** if it starts with "mod."/"stk." or ends with ".mod"/".stk" (case insensitive).
    610 	*/
    611 	if (modFormat == FORMAT_POSSIBLY_STK)
    612 	{
    613 		char *path = unicharToCp850(pathU, false);
    614 		if (path == NULL)
    615 			return false;
    616 
    617 		int32_t pathLen = (int32_t)strlen(path);
    618 
    619 		// get filename from path
    620 		int32_t i = pathLen;
    621 		while (i--)
    622 		{
    623 			if (path[i] == DIR_DELIMITER)
    624 				break;
    625 		}
    626 
    627 		char *filename = path;
    628 		if (i > 0)
    629 			filename += i + 1;
    630 
    631 		int32_t filenameLen = (int32_t)strlen(filename);
    632 		// --------------------------
    633 
    634 		if (filenameLen > 5)
    635 		{
    636 			if (!_strnicmp("mod.", filename, 4) || !_strnicmp("stk.", filename, 4))
    637 			{
    638 				free(path);
    639 				return true;
    640 			}
    641 
    642 			if (!_strnicmp(".mod", &filename[filenameLen-4], 4) || !_strnicmp(".stk", &filename[filenameLen-4], 4))
    643 			{
    644 				free(path);
    645 				return true;
    646 			}
    647 		}
    648 
    649 		free(path);
    650 		return false;
    651 	}
    652 
    653 	return (modFormat != FORMAT_UNKNOWN);
    654 }
    655 
    656 void loadDroppedFile(char *fullPathUTF8, bool songModifiedCheck)
    657 {
    658 	if (ui.sysReqShown || fullPathUTF8 == NULL)
    659 		return;
    660 
    661 	const int32_t fullPathLen = (const int32_t)strlen(fullPathUTF8);
    662 	if (fullPathLen == 0)
    663 		return;
    664 
    665 	UNICHAR *fullPathU = (UNICHAR *)malloc((fullPathLen + 1) * sizeof (UNICHAR));
    666 	if (fullPathU == NULL)
    667 	{
    668 		okBox(0, "System message", "Not enough memory!", NULL);
    669 		return;
    670 	}
    671 
    672 	fullPathU[0] = 0;
    673 
    674 #ifdef _WIN32
    675 	MultiByteToWideChar(CP_UTF8, 0, fullPathUTF8, -1, fullPathU, fullPathLen+1);
    676 #else
    677 	strcpy(fullPathU, fullPathUTF8);
    678 #endif
    679 
    680 	const int32_t filesize = getFileSize(fullPathU);
    681 
    682 	if (filesize == -1) // >2GB
    683 	{
    684 		okBox(0, "System message", "The file is too big and can't be loaded (over 2GB).", NULL);
    685 		free(fullPathU);
    686 		return;
    687 	}
    688 
    689 	if (filesize >= 128L*1024*1024) // 128MB
    690 	{
    691 		if (okBox(2, "System request", "Are you sure you want to load such a big file?", NULL) != 1)
    692 		{
    693 			free(fullPathU);
    694 			return;
    695 		}
    696 	}
    697 
    698 	exitTextEditing();
    699 
    700 	// pass UTF8 to these tests so that we can test file ending in ASCII/ANSI
    701 
    702 	if (fileIsInstr(fullPathU))
    703 	{
    704 		loadInstr(fullPathU);
    705 	}
    706 	else if (fileIsModule(fullPathU))
    707 	{
    708 		if (songModifiedCheck && song.isModified)
    709 		{
    710 			if (!askUnsavedChanges(ASK_TYPE_LOAD_SONG))
    711 			{
    712 				free(fullPathU);
    713 				return;
    714 			}
    715 		}
    716 
    717 		editor.loadMusicEvent = EVENT_LOADMUSIC_DRAGNDROP;
    718 		loadMusic(fullPathU);
    719 	}
    720 	else
    721 	{
    722 		loadSample(fullPathU, editor.curSmp, false);
    723 	}
    724 
    725 	free(fullPathU);
    726 }
    727 
    728 static void handleOldPlayMode(void)
    729 {
    730 	playMode = oldPlayMode;
    731 	if (oldPlayMode != PLAYMODE_IDLE && oldPlayMode != PLAYMODE_EDIT)
    732 		startPlaying(oldPlayMode, 0);
    733 
    734 	songPlaying = (playMode >= PLAYMODE_SONG);
    735 }
    736 
    737 // called from input/video thread after module load thread was finished
    738 void handleLoadMusicEvents(void)
    739 {
    740 	if (!moduleLoaded && !moduleFailedToLoad)
    741 		return; // no event to handle
    742 
    743 	if (moduleFailedToLoad)
    744 	{
    745 		// module failed to load from loading thread
    746 		musicIsLoading = false;
    747 		moduleFailedToLoad = false;
    748 		moduleLoaded = false;
    749 		editor.loadMusicEvent = EVENT_NONE;
    750 		setMouseBusy(false);
    751 		return;
    752 	}
    753 
    754 	if (moduleLoaded)
    755 	{
    756 		// module was successfully loaded from loading thread
    757 
    758 		switch (editor.loadMusicEvent)
    759 		{
    760 			// module dragged and dropped *OR* user double clicked a file associated with FT2 clone
    761 			case EVENT_LOADMUSIC_DRAGNDROP:
    762 			{
    763 				setupLoadedModule();
    764 				if (editor.autoPlayOnDrop)
    765 					startPlaying(PLAYMODE_SONG, 0);
    766 				else
    767 					handleOldPlayMode();
    768 			}
    769 			break;
    770 
    771 			// filename passed as an exe argument *OR* user double clicked a file associated with FT2 clone
    772 			case EVENT_LOADMUSIC_ARGV:
    773 			{
    774 				setupLoadedModule();
    775 				startPlaying(PLAYMODE_SONG, 0);
    776 			}
    777 			break;
    778 
    779 			// module filename pressed in Disk Op.
    780 			case EVENT_LOADMUSIC_DISKOP:
    781 			{
    782 				setupLoadedModule();
    783 				handleOldPlayMode();
    784 			}
    785 			break;
    786 
    787 			default: break;
    788 		}
    789 
    790 		moduleLoaded = false;
    791 		editor.loadMusicEvent = EVENT_NONE;
    792 		musicIsLoading = false;
    793 		mouseAnimOff();
    794 	}
    795 }