1) Don't calculate big LUTs for mixer deltas on init (was just a waste of RAM) 2) The mixer now uses a 64-bit division to calculate max number of safe samples to mix (branchless), instead of using a big hackish LUT for DIV->MUL 3) Module and sample loaders have been refactored so that it's much simpler to write new loaders (or modify the old ones) 4) Added support for importing DIGI Booster (non-Pro) modules 5) Added support for importing 8-channel StarTrekker modules 6) Updated help text 7) Some more minor code change 8) Updated the Visual Studio project and make scripts to include the new loaders * Sorry for not making individual commits for all the changes, but at this point it was simply too difficult for me to do so!
Showing the changes to src/ft2_module_loader.c (1 of 37 files). Show all
Unified · Split
| @@ -10,7 +10,6 @@ · 9 unmodified lines | |||
| 10 | #include <unistd.h> | 10 | #include <unistd.h> |
| 11 | #endif | 11 | #endif |
| 12 | #include "ft2_header.h" | 12 | #include "ft2_header.h" |
| 13 | #include "ft2_config.h" | ||
| 14 | #include "ft2_scopes.h" | 13 | #include "ft2_scopes.h" |
| 15 | #include "ft2_trim.h" | 14 | #include "ft2_trim.h" |
| 16 | #include "ft2_inst_ed.h" | 15 | #include "ft2_inst_ed.h" |
| @@ -24,1857 +23,206 @@ · 7 unmodified lines | |||
| 24 | #include "ft2_midi.h" | 23 | #include "ft2_midi.h" |
| 25 | #include "ft2_events.h" | 24 | #include "ft2_events.h" |
| 26 | #include "ft2_video.h" | 25 | #include "ft2_video.h" |
| 27 | #include "ft2_tables.h" | ||
| 28 | #include "ft2_structs.h" | 26 | #include "ft2_structs.h" |
| 27 | #include "ft2_sysreqs.h" | ||
| 29 | 28 | ||
| 30 | /* This is a *HUGE* mess! | 29 | bool loadDIGI(FILE *f, uint32_t filesize); |
| 31 | ** I hope you never have to modify it, and you probably shouldn't either. | 30 | bool loadMOD(FILE *f, uint32_t filesize); |
| 32 | ** You will experience a lot of headaches if you dig into this file... | 31 | bool loadS3M(FILE *f, uint32_t filesize); |
| 33 | ** If something looks to be wrong, it's probably right! | 32 | bool loadSTK(FILE *f, uint32_t filesize); |
| 34 | ** | 33 | bool loadSTM(FILE *f, uint32_t filesize); |
| 35 | ** The actual module load routines are ported from FT2 and slightly modified. | 34 | bool loadXM(FILE *f, uint32_t filesize); |
| 36 | */ | ||
| 37 | 35 | ||
| 38 | enum | 36 | enum |
| 39 | { | 37 | { |
| 40 | FORMAT_NONE = 0, | 38 | FORMAT_UNKNOWN = 0, |
| 41 | FORMAT_XM = 1, | 39 | FORMAT_POSSIBLY_STK = 1, |
| 42 | FORMAT_MOD = 2, | 40 | FORMAT_XM = 2, |
| 43 | FORMAT_S3M = 3, | 41 | FORMAT_MOD = 3, |
| 44 | FORMAT_STM = 4 | 42 | FORMAT_S3M = 4, |
| 43 | FORMAT_STM = 5, | ||
| 44 | FORMAT_DIGI = 6 | ||
| 45 | }; | 45 | }; |
| 46 | 46 | ||
| 47 | // .MOD types | 47 | // file extensions accepted by Disk Op. in module mode |
| 48 | enum | 48 | char *supportedModExtensions[] = |
| 49 | { | ||
| 50 | FORMAT_MK, // ProTracker or compatible | ||
| 51 | FORMAT_FLT, // StarTrekker (4-channel modules only) | ||
| 52 | FORMAT_FT2, // FT2 (or other trackers, multichannel) | ||
| 53 | FORMAT_STK, // The Ultimate SoundTracker (15 samples) | ||
| 54 | FORMAT_NT, // NoiseTracker | ||
| 55 | FORMAT_HMNT, // His Master's NoiseTracker (special one) | ||
| 56 | |||
| 57 | FORMAT_UNKNOWN // may be The Ultimate Soundtracker (set to FORMAT_STK later) | ||
| 58 | }; | ||
| 59 | |||
| 60 | // DO NOT TOUCH THESE STRUCTS! | ||
| 61 | |||
| 62 | #ifdef _MSC_VER | ||
| 63 | #pragma pack(push) | ||
| 64 | #pragma pack(1) | ||
| 65 | #endif | ||
| 66 | typedef struct songSTMinstrHeaderTyp_t | ||
| 67 | { | ||
| 68 | char name[12]; | ||
| 69 | uint8_t nul, insDisk; | ||
| 70 | uint16_t reserved1, len, repS, repE; | ||
| 71 | uint8_t vol, reserved2; | ||
| 72 | uint16_t rate; | ||
| 73 | int32_t reserved3; | ||
| 74 | uint16_t paraLen; | ||
| 75 | } | ||
| 76 | #ifdef __GNUC__ | ||
| 77 | __attribute__ ((packed)) | ||
| 78 | #endif | ||
| 79 | songSTMinstrHeaderTyp; | ||
| 80 | |||
| 81 | typedef struct songSTMHeaderTyp_t | ||
| 82 | { | ||
| 83 | char name[20], sig[8]; | ||
| 84 | uint8_t id1a, typ; | ||
| 85 | uint8_t verMajor, verMinor; | ||
| 86 | uint8_t tempo, ap, vol, reserved[13]; | ||
| 87 | songSTMinstrHeaderTyp instr[31]; | ||
| 88 | uint8_t songTab[128]; | ||
| 89 | } | ||
| 90 | #ifdef __GNUC__ | ||
| 91 | __attribute__ ((packed)) | ||
| 92 | #endif | ||
| 93 | songSTMHeaderTyp; | ||
| 94 | |||
| 95 | typedef struct songS3MinstrHeaderTyp_t | ||
| 96 | { | ||
| 97 | uint8_t typ; | ||
| 98 | char dosName[12]; | ||
| 99 | uint8_t memSegH; | ||
| 100 | uint16_t memSeg; | ||
| 101 | int32_t len, repS, repE; | ||
| 102 | uint8_t vol, dsk, pack, flags; | ||
| 103 | int32_t c2Spd, res1; | ||
| 104 | uint16_t gusPos; | ||
| 105 | uint8_t res2[6]; | ||
| 106 | char name[28], id[4]; | ||
| 107 | } | ||
| 108 | #ifdef __GNUC__ | ||
| 109 | __attribute__ ((packed)) | ||
| 110 | #endif | ||
| 111 | songS3MinstrHeaderTyp; | ||
| 112 | |||
| 113 | typedef struct songS3MHeaderTyp_t | ||
| 114 | { | ||
| 115 | char name[28]; | ||
| 116 | uint8_t id1a, typ; | ||
| 117 | uint16_t res1; | ||
| 118 | int16_t songTabLen, antInstr, antPatt; | ||
| 119 | uint16_t flags, trackerID, ver; | ||
| 120 | char id[4]; | ||
| 121 | uint8_t globalVol, defSpeed, defTempo, masterVol, res2[12], chanType[32]; | ||
| 122 | } | ||
| 123 | #ifdef __GNUC__ | ||
| 124 | __attribute__ ((packed)) | ||
| 125 | #endif | ||
| 126 | songS3MHeaderTyp; | ||
| 127 | #ifdef _MSC_VER | ||
| 128 | #pragma pack(pop) | ||
| 129 | #endif | ||
| 130 | |||
| 131 | static volatile uint8_t loadedFormat; | ||
| 132 | static volatile bool linearFreqTable, musicIsLoading, moduleLoaded, moduleFailedToLoad; | ||
| 133 | static uint8_t oldPlayMode, pattBuff[12288], packedPattData[65536]; | ||
| 134 | static const uint8_t stmEff[16] = { 0, 0, 11, 0, 10, 2, 1, 3, 4, 7, 0, 5 ,6, 0, 0, 0 }; | ||
| 135 | static SDL_Thread *thread; | ||
| 136 | |||
| 137 | // these temporarily read to, then copied to real struct if load was OK (should not need to be volatile'd) | ||
| 138 | static int16_t pattLensTmp[MAX_PATTERNS]; | ||
| 139 | static uint32_t extraSampleLengths[32-MAX_SMP_PER_INST]; // ModPlug Tracker & OpenMPT supports up to 32 samples per instrument in .XM | ||
| 140 | static tonTyp *pattTmp[MAX_PATTERNS]; | ||
| 141 | static instrTyp *instrTmp[1+256]; | ||
| 142 | static songTyp songTmp; | ||
| 143 | |||
| 144 | static void setupLoadedModule(void); | ||
| 145 | static void freeTmpModule(void); | ||
| 146 | static bool loadInstrHeader(FILE *f, uint16_t i, bool externalThreadFlag); | ||
| 147 | static bool loadInstrSample(FILE *f, uint16_t i, bool externalThreadFlag); | ||
| 148 | void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn); | ||
| 149 | static bool tmpPatternEmpty(uint16_t nr); | ||
| 150 | static bool loadPatterns(FILE *f, uint16_t antPtn, bool externalThreadFlag); | ||
| 151 | |||
| 152 | void checkSampleRepeat(sampleTyp *s); | ||
| 153 | |||
| 154 | // ft2_replayer.c | ||
| 155 | extern const char modSig[32][5]; | ||
| 156 | |||
| 157 | static bool allocateTmpInstr(int16_t nr) | ||
| 158 | { | ||
| 159 | if (instrTmp[nr] != NULL) | ||
| 160 | return false; // already allocated | ||
| 161 | |||
| 162 | instrTyp *p = (instrTyp *)calloc(1, sizeof (instrTyp)); | ||
| 163 | if (p == NULL) | ||
| 164 | return false; | ||
| 165 | |||
| 166 | sampleTyp *s = p->samp; | ||
| 167 | for (int32_t i = 0; i < MAX_SMP_PER_INST; i++, s++) | ||
| 168 | { | ||
| 169 | s->pan = 128; | ||
| 170 | s->vol = 64; | ||
| 171 | } | ||
| 172 | |||
| 173 | instrTmp[nr] = p; | ||
| 174 | return true; | ||
| 175 | } | ||
| 176 | |||
| 177 | #define IS_ID(s, b) !strncmp(s, b, 4) | ||
| 178 | |||
| 179 | static uint8_t getModType(uint8_t *numChannels, const char *id) | ||
| 180 | { | ||
| 181 | uint8_t modFormat = FORMAT_UNKNOWN; | ||
| 182 | *numChannels = 4; | ||
| 183 | |||
| 184 | if (IS_ID("M.K.", id) || IS_ID("M!K!", id) || IS_ID("NSMS", id) || IS_ID("LARD", id) || IS_ID("PATT", id)) | ||
| 185 | { | ||
| 186 | modFormat = FORMAT_MK; // ProTracker or compatible | ||
| 187 | } | ||
| 188 | else if (id[1] == 'C' && id[2] == 'H' && id[3] == 'N') | ||
| 189 | { | ||
| 190 | modFormat = FORMAT_FT2; // FT2 or generic multichannel | ||
| 191 | *numChannels = id[0] - '0'; | ||
| 192 | } | ||
| 193 | else if (id[2] == 'C' && (id[3] == 'H' || id[3] == 'N')) | ||
| 194 | { | ||
| 195 | modFormat = FORMAT_FT2; // FT2 or generic multichannel | ||
| 196 | *numChannels = ((id[0] - '0') * 10) + (id[1] - '0'); | ||
| 197 | } | ||
| 198 | else if (IS_ID("FLT4", id)) | ||
| 199 | { | ||
| 200 | modFormat = FORMAT_FLT; // StarTrekker (4-channel modules only) | ||
| 201 | } | ||
| 202 | else if (IS_ID("N.T.", id)) | ||
| 203 | { | ||
| 204 | modFormat = FORMAT_NT; // NoiseTracker | ||
| 205 | } | ||
| 206 | else if (IS_ID("M&K!", id) || IS_ID("FEST", id)) | ||
| 207 | { | ||
| 208 | modFormat = FORMAT_HMNT; // His Master's NoiseTracker | ||
| 209 | } | ||
| 210 | |||
| 211 | return modFormat; | ||
| 212 | } | ||
| 213 | |||
| 214 | static bool loadMusicMOD(FILE *f, uint32_t fileLength, bool externalThreadFlag) | ||
| 215 | { | ||
| 216 | char ID[16]; | ||
| 217 | uint8_t bytes[4], modFormat, numChannels; | ||
| 218 | int16_t i, j, k, ai; | ||
| 219 | uint16_t a, b, period; | ||
| 220 | tonTyp *ton; | ||
| 221 | sampleTyp *s; | ||
| 222 | songMOD31HeaderTyp h_MOD31; | ||
| 223 | songMOD15HeaderTyp h_MOD15; | ||
| 224 | int16_t (*showMsg)(int16_t, const char *, const char *); | ||
| 225 | |||
| 226 | showMsg = externalThreadFlag ? okBoxThreadSafe : okBox; | ||
| 227 | |||
| 228 | bool veryLateSTKVerFlag = false; // "DFJ SoundTracker III" nad later | ||
| 229 | bool lateSTKVerFlag = false; // "TJC SoundTracker II" and later | ||
| 230 | bool mightBeSTK = false; | ||
| 231 | |||
| 232 | memset(&songTmp, 0, sizeof (songTmp)); | ||
| 233 | memset(&h_MOD31, 0, sizeof (songMOD31HeaderTyp)); | ||
| 234 | memset(&h_MOD15, 0, sizeof (songMOD15HeaderTyp)); | ||
| 235 | |||
| 236 | // start loading MOD | ||
| 237 | |||
| 238 | loadedFormat = FORMAT_MOD; | ||
| 239 | |||
| 240 | rewind(f); | ||
| 241 | fread(ID, 1, 16, f); | ||
| 242 | fseek(f, 0x1D, SEEK_SET); | ||
| 243 | fread(bytes, 1, 1, f); | ||
| 244 | rewind(f); | ||
| 245 | |||
| 246 | // since .mod is the last format tested, check if the file is an .it module (Impulse Tracker) | ||
| 247 | if (!memcmp(ID, "IMPM", 4) && bytes[0] == 0) | ||
| 248 | { | ||
| 249 | showMsg(0, "System message", "Error: Impulse Tracker modules are not supported!"); | ||
| 250 | goto modLoadError; | ||
| 251 | } | ||
| 252 | |||
| 253 | // check if the file to load is a WAV, if so reject it | ||
| 254 | if (!memcmp(ID, "RIFF", 4) && !memcmp(&ID[8], "WAVEfmt", 7)) | ||
| 255 | { | ||
| 256 | showMsg(0, "System message", "Error: Can't load a .wav file as a module!"); | ||
| 257 | goto modLoadError; | ||
| 258 | } | ||
| 259 | |||
| 260 | if (fileLength < 1596 || fileLength > 20842494) // minimum and maximum possible size for a supported .mod | ||
| 261 | { | ||
| 262 | showMsg(0, "System message", "Error: This file is either not a module, or is not supported."); | ||
| 263 | goto modLoadError; | ||
| 264 | } | ||
| 265 | |||
| 266 | if (fread(&h_MOD31, 1, sizeof (h_MOD31), f) != sizeof (h_MOD31)) | ||
| 267 | { | ||
| 268 | showMsg(0, "System message", "Error: This file is either not a module, or is not supported."); | ||
| 269 | goto modLoadError; | ||
| 270 | } | ||
| 271 | |||
| 272 | modFormat = getModType(&numChannels, h_MOD31.sig); | ||
| 273 | |||
| 274 | if (modFormat == FORMAT_FLT && numChannels == 8) | ||
| 275 | { | ||
| 276 | showMsg(0, "System message", "8-channel Startrekker modules are not supported!"); | ||
| 277 | goto modLoadError; | ||
| 278 | } | ||
| 279 | |||
| 280 | if (modFormat != FORMAT_UNKNOWN) | ||
| 281 | { | ||
| 282 | if (fileLength < sizeof (h_MOD31)) | ||
| 283 | { | ||
| 284 | showMsg(0, "System message", "Error: This file is either not a module, or is not supported."); | ||
| 285 | goto modLoadError; | ||
| 286 | } | ||
| 287 | |||
| 288 | songTmp.antChn = numChannels; | ||
| 289 | songTmp.len = h_MOD31.len; | ||
| 290 | songTmp.repS = h_MOD31.repS; | ||
| 291 | memcpy(songTmp.songTab, h_MOD31.songTab, 128); | ||
| 292 | ai = 31; | ||
| 293 | } | ||
| 294 | else | ||
| 295 | { | ||
| 296 | mightBeSTK = true; | ||
| 297 | if (fileLength < sizeof (h_MOD15)) | ||
| 298 | { | ||
| 299 | showMsg(0, "System message", "Error: This file is not a module!"); | ||
| 300 | goto modLoadError; | ||
| 301 | } | ||
| 302 | |||
| 303 | rewind(f); | ||
| 304 | if (fread(&h_MOD15, 1, sizeof (h_MOD15), f) != sizeof (h_MOD15)) | ||
| 305 | { | ||
| 306 | showMsg(0, "System message", "Error: This file is either not a module, or is not supported."); | ||
| 307 | goto modLoadError; | ||
| 308 | } | ||
| 309 | |||
| 310 | songTmp.antChn = numChannels; | ||
| 311 | songTmp.len = h_MOD15.len; | ||
| 312 | songTmp.repS = h_MOD15.repS; | ||
| 313 | memcpy(songTmp.songTab, h_MOD15.songTab, 128); | ||
| 314 | ai = 15; | ||
| 315 | } | ||
| 316 | |||
| 317 | if (modFormat == FORMAT_MK && songTmp.len == 129) | ||
| 318 | songTmp.len = 127; // fixes a specific copy of beatwave.mod (FIXME: Do we want to keep this digsuting hack?) | ||
| 319 | |||
| 320 | if (songTmp.antChn == 0 || songTmp.len < 1 || songTmp.len > 128 || (mightBeSTK && songTmp.repS > 220)) | ||
| 321 | { | ||
| 322 | showMsg(0, "System message", "Error: This file is either not a module, or is not supported."); | ||
| 323 | goto modLoadError; | ||
| 324 | } | ||
| 325 | |||
| 326 | for (a = 0; a < ai; a++) | ||
| 327 | { | ||
| 328 | songMODInstrHeaderTyp *smp = &h_MOD31.instr[a]; | ||
| 329 | |||
| 330 | if (modFormat != FORMAT_HMNT) // most of "His Master's Noisetracker" songs have junk sample names, so let's not load them | ||
| 331 | { | ||
| 332 | // trim off spaces at end of name | ||
| 333 | for (i = 21; i >= 0; i--) | ||
| 334 | { | ||
| 335 | if (smp->name[i] == ' ' || smp->name[i] == 0x1A) | ||
| 336 | smp->name[i] = '\0'; | ||
| 337 | else | ||
| 338 | break; | ||
| 339 | } | ||
| 340 | |||
| 341 | memcpy(songTmp.instrName[1+a], smp->name, 22); | ||
| 342 | songTmp.instrName[1+a][22] = '\0'; | ||
| 343 | } | ||
| 344 | |||
| 345 | /* Only late versions of Ultimate SoundTracker could have samples larger than 9999 bytes. | ||
| 346 | ** If found, we know for sure that this is a late STK module. | ||
| 347 | */ | ||
| 348 | if (mightBeSTK && 2*SWAP16(smp->len) > 9999) | ||
| 349 | lateSTKVerFlag = true; | ||
| 350 | } | ||
| 351 | |||
| 352 | songTmp.speed = 125; | ||
| 353 | if (mightBeSTK) | ||
| 354 | { | ||
| 355 | /* If we're still here at this point and the mightBeSTK flag is set, | ||
| 356 | ** then it's most likely a proper The Ultimate SoundTracker (STK/UST) module. | ||
| 357 | */ | ||
| 358 | modFormat = FORMAT_STK; | ||
| 359 | |||
| 360 | if (songTmp.repS == 0) | ||
| 361 | songTmp.repS = 120; | ||
| 362 | |||
| 363 | // jjk55.mod by Jesper Kyd has a bogus STK tempo value that should be ignored (hackish!) | ||
| 364 | if (!strcmp("jjk55", h_MOD31.name)) | ||
| 365 | songTmp.repS = 120; | ||
| 366 | |||
| 367 | // The "restart pos" field in STK is the inital tempo (must be converted to BPM first) | ||
| 368 | if (songTmp.repS != 120) // 120 is a special case and means 50Hz (125BPM) | ||
| 369 | { | ||
| 370 | if (songTmp.repS > 220) | ||
| 371 | songTmp.repS = 220; | ||
| 372 | |||
| 373 | // convert UST tempo to BPM | ||
| 374 | uint16_t ciaPeriod = (240 - songTmp.repS) * 122; | ||
| 375 | double dHz = 709379.0 / ciaPeriod; | ||
| 376 | int32_t BPM = (int32_t)((dHz * 2.5) + 0.5); | ||
| 377 | |||
| 378 | songTmp.speed = (uint16_t)BPM; | ||
| 379 | } | ||
| 380 | |||
| 381 | songTmp.repS = 0; | ||
| 382 | } | ||
| 383 | else if (songTmp.repS >= songTmp.len) | ||
| 384 | { | ||
| 385 | songTmp.repS = 0; | ||
| 386 | } | ||
| 387 | |||
| 388 | // trim off spaces at end of name | ||
| 389 | for (i = 19; i >= 0; i--) | ||
| 390 | { | ||
| 391 | if (h_MOD31.name[i] == ' ' || h_MOD31.name[i] == 0x1A) | ||
| 392 | h_MOD31.name[i] = '\0'; | ||
| 393 | else | ||
| 394 | break; | ||
| 395 | } | ||
| 396 | |||
| 397 | memcpy(songTmp.name, h_MOD31.name, 20); | ||
| 398 | songTmp.name[20] = '\0'; | ||
| 399 | |||
| 400 | // count number of patterns | ||
| 401 | b = 0; | ||
| 402 | for (a = 0; a < 128; a++) | ||
| 403 | { | ||
| 404 | if (songTmp.songTab[a] > b) | ||
| 405 | b = songTmp.songTab[a]; | ||
| 406 | } | ||
| 407 | b++; | ||
| 408 | |||
| 409 | for (a = 0; a < b; a++) | ||
| 410 | { | ||
| 411 | pattTmp[a] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); | ||
| 412 | if (pattTmp[a] == NULL) | ||
| 413 | { | ||
| 414 | showMsg(0, "System message", "Not enough memory!"); | ||
| 415 | goto modLoadError; | ||
| 416 | } | ||
| 417 | |||
| 418 | pattLensTmp[a] = 64; | ||
| 419 | for (j = 0; j < 64; j++) | ||
| 420 | { | ||
| 421 | for (k = 0; k < songTmp.antChn; k++) | ||
| 422 | { | ||
| 423 | ton = &pattTmp[a][(j * MAX_VOICES) + k]; | ||
| 424 | |||
| 425 | if (fread(bytes, 1, 4, f) != 4) | ||
| 426 | { | ||
| 427 | showMsg(0, "System message", "Error: This file is either not a module, or is not supported."); | ||
| 428 | goto modLoadError; | ||
| 429 | } | ||
| 430 | |||
| 431 | // period to note | ||
| 432 | period = ((bytes[0] & 0x0F) << 8) | bytes[1]; | ||
| 433 | for (i = 0; i < 8*12; i++) | ||
| 434 | { | ||
| 435 | if (period >= amigaPeriod[i]) | ||
| 436 | { | ||
| 437 | ton->ton = (uint8_t)i + 1; | ||
| 438 | break; | ||
| 439 | } | ||
| 440 | } | ||
| 441 | |||
| 442 | ton->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4); | ||
| 443 | ton->effTyp = bytes[2] & 0x0F; | ||
| 444 | ton->eff = bytes[3]; | ||
| 445 | |||
| 446 | if (modFormat == FORMAT_STK) | ||
| 447 | { | ||
| 448 | if (ton->effTyp == 0xC || ton->effTyp == 0xD || ton->effTyp == 0xE) | ||
| 449 | { | ||
| 450 | // "TJC SoundTracker II" and later | ||
| 451 | lateSTKVerFlag = true; | ||
| 452 | } | ||
| 453 | |||
| 454 | if (ton->effTyp == 0xF) | ||
| 455 | { | ||
| 456 | // "DFJ SoundTracker III" and later | ||
| 457 | lateSTKVerFlag = true; | ||
| 458 | veryLateSTKVerFlag = true; | ||
| 459 | } | ||
| 460 | } | ||
| 461 | |||
| 462 | if (ton->effTyp == 0xC) | ||
| 463 | { | ||
| 464 | if (ton->eff > 64) | ||
| 465 | ton->eff = 64; | ||
| 466 | } | ||
| 467 | else if (ton->effTyp == 0x1) | ||
| 468 | { | ||
| 469 | if (ton->eff == 0) | ||
| 470 | ton->effTyp = 0; | ||
| 471 | } | ||
| 472 | else if (ton->effTyp == 0x2) | ||
| 473 | { | ||
| 474 | if (ton->eff == 0) | ||
| 475 | ton->effTyp = 0; | ||
| 476 | } | ||
| 477 | else if (ton->effTyp == 0x5) | ||
| 478 | { | ||
| 479 | if (ton->eff == 0) | ||
| 480 | ton->effTyp = 0x3; | ||
| 481 | } | ||
| 482 | else if (ton->effTyp == 0x6) | ||
| 483 | { | ||
| 484 | if (ton->eff == 0) | ||
| 485 | ton->effTyp = 0x4; | ||
| 486 | } | ||
| 487 | else if (ton->effTyp == 0xA) | ||
| 488 | { | ||
| 489 | if (ton->eff == 0) | ||
| 490 | ton->effTyp = 0; | ||
| 491 | } | ||
| 492 | else if (ton->effTyp == 0xE) | ||
| 493 | { | ||
| 494 | // check if certain E commands are empty | ||
| 495 | if (ton->eff == 0x10 || ton->eff == 0x20 || ton->eff == 0xA0 || ton->eff == 0xB0) | ||
| 496 | { | ||
| 497 | ton->effTyp = 0; | ||
| 498 | ton->eff = 0; | ||
| 499 | } | ||
| 500 | } | ||
| 501 | } | ||
| 502 | } | ||
| 503 | |||
| 504 | if (tmpPatternEmpty(a)) | ||
| 505 | { | ||
| 506 | if (pattTmp[a] != NULL) | ||
| 507 | { | ||
| 508 | free(pattTmp[a]); | ||
| 509 | pattTmp[a] = NULL; | ||
| 510 | } | ||
| 511 | } | ||
| 512 | } | ||
| 513 | |||
| 514 | // pattern command conversion for non-PT formats | ||
| 515 | if (modFormat == FORMAT_STK || modFormat == FORMAT_NT || modFormat == FORMAT_HMNT || modFormat == FORMAT_FLT) | ||
| 516 | { | ||
| 517 | for (a = 0; a < b; a++) | ||
| 518 | { | ||
| 519 | if (pattTmp[a] == NULL) | ||
| 520 | continue; | ||
| 521 | |||
| 522 | for (j = 0; j < 64; j++) | ||
| 523 | { | ||
| 524 | for (k = 0; k < songTmp.antChn; k++) | ||
| 525 | { | ||
| 526 | ton = &pattTmp[a][(j * MAX_VOICES) + k]; | ||
| 527 | |||
| 528 | if (modFormat == FORMAT_NT || modFormat == FORMAT_HMNT) | ||
| 529 | { | ||
| 530 | // any Dxx == D00 in NT/HMNT | ||
| 531 | if (ton->effTyp == 0xD) | ||
| 532 | ton->eff = 0; | ||
| 533 | |||
| 534 | // effect F with param 0x00 does nothing in NT/HMNT | ||
| 535 | if (ton->effTyp == 0xF && ton->eff == 0) | ||
| 536 | ton->effTyp = 0; | ||
| 537 | } | ||
| 538 | else if (modFormat == FORMAT_FLT) // Startrekker | ||
| 539 | { | ||
| 540 | if (ton->effTyp == 0xE) // remove unsupported "assembly macros" command | ||
| 541 | { | ||
| 542 | ton->effTyp = 0; | ||
| 543 | ton->eff = 0; | ||
| 544 | } | ||
| 545 | |||
| 546 | // Startrekker is always in vblank mode, and limits speed to 0x1F | ||
| 547 | if (ton->effTyp == 0xF && ton->eff > 0x1F) | ||
| 548 | ton->eff = 0x1F; | ||
| 549 | } | ||
| 550 | else if (modFormat == FORMAT_STK) | ||
| 551 | { | ||
| 552 | // convert STK effects to PT effects | ||
| 553 | |||
| 554 | if (!lateSTKVerFlag) | ||
| 555 | { | ||
| 556 | // old SoundTracker 1.x commands | ||
| 557 | |||
| 558 | if (ton->effTyp == 1) | ||
| 559 | { | ||
| 560 | // arpeggio | ||
| 561 | ton->effTyp = 0; | ||
| 562 | } | ||
| 563 | else if (ton->effTyp == 2) | ||
| 564 | { | ||
| 565 | // pitch slide | ||
| 566 | if (ton->eff & 0xF0) | ||
| 567 | { | ||
| 568 | // pitch slide down | ||
| 569 | ton->effTyp = 2; | ||
| 570 | ton->eff >>= 4; | ||
| 571 | } | ||
| 572 | else if (ton->eff & 0x0F) | ||
| 573 | { | ||
| 574 | // pitch slide up | ||
| 575 | ton->effTyp = 1; | ||
| 576 | } | ||
| 577 | } | ||
| 578 | } | ||
| 579 | else | ||
| 580 | { | ||
| 581 | // "DFJ SoundTracker II" or later | ||
| 582 | |||
| 583 | if (ton->effTyp == 0xD) | ||
| 584 | { | ||
| 585 | if (veryLateSTKVerFlag) // "DFJ SoundTracker III" or later | ||
| 586 | { | ||
| 587 | // pattern break w/ no param (param must be cleared to fix some songs) | ||
| 588 | ton->eff = 0; | ||
| 589 | } | ||
| 590 | else | ||
| 591 | { | ||
| 592 | // volume slide | ||
| 593 | ton->effTyp = 0xA; | ||
| 594 | } | ||
| 595 | } | ||
| 596 | } | ||
| 597 | |||
| 598 | // effect F with param 0x00 does nothing in UST/STK (I think?) | ||
| 599 | if (ton->effTyp == 0xF && ton->eff == 0) | ||
| 600 | ton->effTyp = 0; | ||
| 601 | } | ||
| 602 | } | ||
| 603 | } | ||
| 604 | } | ||
| 605 | } | ||
| 606 | |||
| 607 | for (a = 0; a < ai; a++) | ||
| 608 | { | ||
| 609 | if (h_MOD31.instr[a].len == 0) | ||
| 610 | continue; | ||
| 611 | |||
| 612 | if (!allocateTmpInstr(1+a)) | ||
| 613 | { | ||
| 614 | showMsg(0, "System message", "Not enough memory!"); | ||
| 615 | goto modLoadError; | ||
| 616 | } | ||
| 617 | |||
| 618 | setNoEnvelope(instrTmp[1+a]); | ||
| 619 | |||
| 620 | s = &instrTmp[1+a]->samp[0]; | ||
| 621 | |||
| 622 | s->len = 2 * SWAP16(h_MOD31.instr[a].len); | ||
| 623 | |||
| 624 | if (modFormat != FORMAT_HMNT) // most of "His Master's Noisetracker" songs have junk sample names, so let's not load them | ||
| 625 | memcpy(s->name, songTmp.instrName[1+a], 22); | ||
| 626 | |||
| 627 | if (modFormat == FORMAT_HMNT) // finetune in "His Master's NoiseTracker" is different | ||
| 628 | h_MOD31.instr[a].fine = (uint8_t)((-h_MOD31.instr[a].fine & 0x1F) >> 1); // one more bit of precision, + inverted | ||
| 629 | |||
| 630 | if (modFormat != FORMAT_STK) | ||
| 631 | s->fine = 8 * ((2 * ((h_MOD31.instr[a].fine & 0xF) ^ 8)) - 16); | ||
| 632 | |||
| 633 | s->pan = 128; | ||
| 634 | |||
| 635 | s->vol = h_MOD31.instr[a].vol; | ||
| 636 | if (s->vol > 64) | ||
| 637 | s->vol = 64; | ||
| 638 | |||
| 639 | s->repS = 2 * SWAP16(h_MOD31.instr[a].repS); | ||
| 640 | s->repL = 2 * SWAP16(h_MOD31.instr[a].repL); | ||
| 641 | |||
| 642 | if (s->repL < 2) | ||
| 643 | s->repL = 2; | ||
| 644 | |||
| 645 | // in The Ultimate SoundTracker, sample loop start is in bytes, not words | ||
| 646 | if (modFormat == FORMAT_STK) | ||
| 647 | s->repS >>= 1; | ||
| 648 | |||
| 649 | // fix for poorly converted STK (< v2.5) -> PT/NT modules (FIXME: Worth keeping or not?) | ||
| 650 | if (modFormat != FORMAT_STK && s->repL > 2 && s->repS+s->repL > s->len) | ||
| 651 | { | ||
| 652 | if ((s->repS>>1)+s->repL <= s->len) | ||
| 653 | s->repS >>= 1; | ||
| 654 | } | ||
| 655 | |||
| 656 | // fix overflown loop | ||
| 657 | if (s->repS+s->repL > s->len) | ||
| 658 | { | ||
| 659 | if (s->repS >= s->len) | ||
| 660 | { | ||
| 661 | s->repS = 0; | ||
| 662 | s->repL = 0; | ||
| 663 | } | ||
| 664 | else | ||
| 665 | { | ||
| 666 | s->repL = s->len - s->repS; | ||
| 667 | } | ||
| 668 | } | ||
| 669 | |||
| 670 | if (s->repS+s->repL > 2) | ||
| 671 | s->typ = 1; // enable loop | ||
| 672 | |||
| 673 | /* For Ultimate SoundTracker modules, only the loop area of a looped sample is played. | ||
| 674 | ** Skip loading of eventual data present before loop start. | ||
| 675 | */ | ||
| 676 | if (modFormat == FORMAT_STK && s->repS > 0 && s->repL < s->len) | ||
| 677 | { | ||
| 678 | s->len -= s->repS; | ||
| 679 | fseek(f, s->repS, SEEK_CUR); | ||
| 680 | s->repS = 0; | ||
| 681 | } | ||
| 682 | |||
| 683 | s->pek = NULL; | ||
| 684 | s->origPek = (int8_t *)malloc(s->len + LOOP_FIX_LEN); | ||
| 685 | if (s->origPek == NULL) | ||
| 686 | { | ||
| 687 | showMsg(0, "System message", "Not enough memory!"); | ||
| 688 | goto modLoadError; | ||
| 689 | } | ||
| 690 | |||
| 691 | s->pek = s->origPek + SMP_DAT_OFFSET; | ||
| 692 | |||
| 693 | int32_t bytesRead = (int32_t)fread(s->pek, 1, s->len, f); | ||
| 694 | if (bytesRead < s->len) | ||
| 695 | { | ||
| 696 | int32_t bytesToClear = s->len - bytesRead; | ||
| 697 | memset(&s->pek[bytesRead], 0, bytesToClear); | ||
| 698 | } | ||
| 699 | |||
| 700 | // clear repL and repS on non-looping samples... | ||
| 701 | if (s->typ == 0) | ||
| 702 | { | ||
| 703 | s->repL = 0; | ||
| 704 | s->repS = 0; | ||
| 705 | } | ||
| 706 | |||
| 707 | fixSample(s); | ||
| 708 | } | ||
| 709 | |||
| 710 | fclose(f); | ||
| 711 | |||
| 712 | songTmp.initialTempo = songTmp.tempo = 6; | ||
| 713 | |||
| 714 | moduleLoaded = true; | ||
| 715 | return true; | ||
| 716 | |||
| 717 | modLoadError: | ||
| 718 | fclose(f); | ||
| 719 | freeTmpModule(); | ||
| 720 | moduleFailedToLoad = true; | ||
| 721 | return false; | ||
| 722 | } | ||
| 723 | |||
| 724 | static uint8_t stmTempoToBPM(uint8_t tempo) // ported from original ST2.3 replayer code | ||
| 725 | { | ||
| 726 | const uint8_t slowdowns[16] = { 140, 50, 25, 15, 10, 7, 6, 4, 3, 3, 2, 2, 2, 2, 1, 1 }; | ||
| 727 | uint16_t hz = 50; | ||
| 728 | |||
| 729 | hz -= ((slowdowns[tempo >> 4] * (tempo & 15)) >> 4); // can and will underflow | ||
| 730 | |||
| 731 | const uint32_t bpm = (hz << 1) + (hz >> 1); // BPM = hz * 2.5 | ||
| 732 | return (uint8_t)CLAMP(bpm, 32, 255); // result can be slightly off, but close enough... | ||
| 733 | } | ||
| 734 | |||
| 735 | static bool loadMusicSTM(FILE *f, uint32_t fileLength, bool externalThreadFlag) | ||
| 736 | { | ||
| 737 | uint8_t typ, tempo; | ||
| 738 | int16_t i, j, k, ap, tmp; | ||
| 739 | uint16_t a; | ||
| 740 | tonTyp *ton; | ||
| 741 | sampleTyp *s; | ||
| 742 | songSTMHeaderTyp h_STM; | ||
| 743 | int16_t (*showMsg)(int16_t, const char *, const char *); | ||
| 744 | |||
| 745 | showMsg = externalThreadFlag ? okBoxThreadSafe : okBox; | ||
| 746 | |||
| 747 | rewind(f); | ||
| 748 | |||
| 749 | // start loading STM | ||
| 750 | |||
| 751 | if (fread(&h_STM, 1, sizeof (h_STM), f) != sizeof (h_STM)) | ||
| 752 | return loadMusicMOD(f, fileLength, externalThreadFlag); // file is not a .stm, try to load as .mod | ||
| 753 | |||
| 754 | if (memcmp(h_STM.sig, "!Scream!", 8) && memcmp(h_STM.sig, "BMOD2STM", 8) && | ||
| 755 | memcmp(h_STM.sig, "WUZAMOD!", 8) && memcmp(h_STM.sig, "SWavePro", 8)) | ||
| 756 | { | ||
| 757 | return loadMusicMOD(f, fileLength, externalThreadFlag); // file is not a .stm, try to load as .mod | ||
| 758 | } | ||
| 759 | |||
| 760 | loadedFormat = FORMAT_STM; | ||
| 761 | |||
| 762 | if (h_STM.verMinor == 0 || h_STM.typ != 2) | ||
| 763 | { | ||
| 764 | showMsg(0, "System message", "Error loading .stm: Incompatible module!"); | ||
| 765 | goto stmLoadError; | ||
| 766 | } | ||
| 767 | |||
| 768 | songTmp.antChn = 4; | ||
| 769 | memcpy(songTmp.songTab, h_STM.songTab, 128); | ||
| 770 | |||
| 771 | i = 0; | ||
| 772 | while (i < 128 && songTmp.songTab[i] < 99) i++; | ||
| 773 | songTmp.len = i + (i == 0); | ||
| 774 | |||
| 775 | if (songTmp.len < 255) | ||
| 776 | memset(&songTmp.songTab[songTmp.len], 0, 256 - songTmp.len); | ||
| 777 | |||
| 778 | // trim off spaces at end of name | ||
| 779 | for (i = 19; i >= 0; i--) | ||
| 780 | { | ||
| 781 | if (h_STM.name[i] == ' ' || h_STM.name[i] == 0x1A) | ||
| 782 | h_STM.name[i] = '\0'; | ||
| 783 | else | ||
| 784 | break; | ||
| 785 | } | ||
| 786 | |||
| 787 | memcpy(songTmp.name, h_STM.name, 20); | ||
| 788 | songTmp.name[20] = '\0'; | ||
| 789 | |||
| 790 | tempo = h_STM.tempo; | ||
| 791 | if (h_STM.verMinor < 21) | ||
| 792 | tempo = ((tempo / 10) << 4) + (tempo % 10); | ||
| 793 | |||
| 794 | if (tempo == 0) | ||
| 795 | tempo = 96; | ||
| 796 | |||
| 797 | songTmp.initialTempo = songTmp.tempo = CLAMP(h_STM.tempo >> 4, 1, 31); | ||
| 798 | songTmp.speed = stmTempoToBPM(tempo); | ||
| 799 | |||
| 800 | if (h_STM.verMinor > 10) | ||
| 801 | songTmp.globVol = MIN(h_STM.vol, 64); | ||
| 802 | |||
| 803 | ap = h_STM.ap; | ||
| 804 | for (i = 0; i < ap; i++) | ||
| 805 | { | ||
| 806 | pattTmp[i] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); | ||
| 807 | if (pattTmp[i] == NULL) | ||
| 808 | { | ||
| 809 | showMsg(0, "System message", "Not enough memory!"); | ||
| 810 | goto stmLoadError; | ||
| 811 | } | ||
| 812 | |||
| 813 | pattLensTmp[i] = 64; | ||
| 814 | if (fread(pattBuff, 64 * 4 * 4, 1, f) != 1) | ||
| 815 | { | ||
| 816 | showMsg(0, "System message", "General I/O error during loading!"); | ||
| 817 | goto stmLoadError; | ||
| 818 | } | ||
| 819 | |||
| 820 | a = 0; | ||
| 821 | for (j = 0; j < 64; j++) | ||
| 822 | { | ||
| 823 | for (k = 0; k < 4; k++) | ||
| 824 | { | ||
| 825 | ton = &pattTmp[i][(j * MAX_VOICES) + k]; | ||
| 826 | |||
| 827 | if (pattBuff[a] == 254) | ||
| 828 | { | ||
| 829 | ton->ton = 97; | ||
| 830 | } | ||
| 831 | else if (pattBuff[a] < 96) | ||
| 832 | { | ||
| 833 | ton->ton = (12 * (pattBuff[a] >> 4)) + (25 + (pattBuff[a] & 0x0F)); | ||
| 834 | if (ton->ton > 96) | ||
| 835 | ton->ton = 0; | ||
| 836 | } | ||
| 837 | else | ||
| 838 | { | ||
| 839 | ton->ton = 0; | ||
| 840 | } | ||
| 841 | |||
| 842 | ton->instr = pattBuff[a + 1] >> 3; | ||
| 843 | typ = (pattBuff[a + 1] & 7) + ((pattBuff[a + 2] & 0xF0) >> 1); | ||
| 844 | if (typ <= 64) | ||
| 845 | ton->vol = typ + 0x10; | ||
| 846 | |||
| 847 | ton->eff = pattBuff[a + 3]; | ||
| 848 | |||
| 849 | tmp = pattBuff[a + 2] & 0x0F; | ||
| 850 | if (tmp == 1) | ||
| 851 | { | ||
| 852 | ton->effTyp = 15; | ||
| 853 | |||
| 854 | if (h_STM.verMinor < 21) | ||
| 855 | ton->eff = ((ton->eff / 10) << 4) + (ton->eff % 10); | ||
| 856 | |||
| 857 | ton->eff >>= 4; | ||
| 858 | } | ||
| 859 | else if (tmp == 3) | ||
| 860 | { | ||
| 861 | ton->effTyp = 13; | ||
| 862 | ton->eff = 0; | ||
| 863 | } | ||
| 864 | else if (tmp == 2 || (tmp >= 4 && tmp <= 12)) | ||
| 865 | { | ||
| 866 | ton->effTyp = stmEff[tmp]; | ||
| 867 | if (ton->effTyp == 0xA) | ||
| 868 | { | ||
| 869 | if (ton->eff & 0x0F) | ||
| 870 | ton->eff &= 0x0F; | ||
| 871 | else | ||
| 872 | ton->eff &= 0xF0; | ||
| 873 | } | ||
| 874 | } | ||
| 875 | else | ||
| 876 | { | ||
| 877 | ton->eff = 0; | ||
| 878 | } | ||
| 879 | |||
| 880 | if (ton->effTyp > 35) | ||
| 881 | { | ||
| 882 | ton->effTyp = 0; | ||
| 883 | ton->eff = 0; | ||
| 884 | } | ||
| 885 | |||
| 886 | a += 4; | ||
| 887 | } | ||
| 888 | } | ||
| 889 | |||
| 890 | if (tmpPatternEmpty(i)) | ||
| 891 | { | ||
| 892 | if (pattTmp[i] != NULL) | ||
| 893 | { | ||
| 894 | free(pattTmp[i]); | ||
| 895 | pattTmp[i] = NULL; | ||
| 896 | } | ||
| 897 | } | ||
| 898 | } | ||
| 899 | |||
| 900 | for (i = 0; i < 31; i++) | ||
| 901 | { | ||
| 902 | // trim off spaces at end of name | ||
| 903 | for (j = 11; j >= 0; j--) | ||
| 904 | { | ||
| 905 | if (h_STM.instr[i].name[j] == ' ' || h_STM.instr[i].name[j] == 0x1A) | ||
| 906 | h_STM.instr[i].name[j] = '\0'; | ||
| 907 | else | ||
| 908 | break; | ||
| 909 | } | ||
| 910 | |||
| 911 | memset(&songTmp.instrName[1+i], 0, sizeof (songTmp.instrName[1+i])); | ||
| 912 | memcpy(&songTmp.instrName[1+i], h_STM.instr[i].name, 12); | ||
| 913 | |||
| 914 | if (h_STM.instr[i].len != 0 && h_STM.instr[i].reserved1 != 0) | ||
| 915 | { | ||
| 916 | allocateTmpInstr(1 + i); | ||
| 917 | setNoEnvelope(instrTmp[i]); | ||
| 918 | |||
| 919 | s = &instrTmp[1+i]->samp[0]; | ||
| 920 | |||
| 921 | s->pek = NULL; | ||
| 922 | s->origPek = (int8_t *)malloc(h_STM.instr[i].len + LOOP_FIX_LEN); | ||
| 923 | if (s->origPek == NULL) | ||
| 924 | { | ||
| 925 | showMsg(0, "System message", "Not enough memory!"); | ||
| 926 | goto stmLoadError; | ||
| 927 | } | ||
| 928 | |||
| 929 | s->pek = s->origPek + SMP_DAT_OFFSET; | ||
| 930 | |||
| 931 | s->len = h_STM.instr[i].len; | ||
| 932 | tuneSample(s, h_STM.instr[i].rate, false); | ||
| 933 | s->vol = h_STM.instr[i].vol; | ||
| 934 | s->repS = h_STM.instr[i].repS; | ||
| 935 | s->repL = h_STM.instr[i].repE - h_STM.instr[i].repS; | ||
| 936 | s->pan = 128; | ||
| 937 | |||
| 938 | if (s->repS < s->len && h_STM.instr[i].repE > s->repS && h_STM.instr[i].repE != 0xFFFF) | ||
| 939 | { | ||
| 940 | if (s->repS+s->repL > s->len) | ||
| 941 | s->repL = s->len - s->repS; | ||
| 942 | |||
| 943 | s->typ = 1; // enable loop | ||
| 944 | } | ||
| 945 | else | ||
| 946 | { | ||
| 947 | s->repS = 0; | ||
| 948 | s->repL = 0; | ||
| 949 | s->typ = 0; | ||
| 950 | } | ||
| 951 | |||
| 952 | if (s->vol > 64) | ||
| 953 | s->vol = 64; | ||
| 954 | |||
| 955 | if (fread(s->pek, s->len, 1, f) != 1) | ||
| 956 | { | ||
| 957 | showMsg(0, "System message", "General I/O error during loading! Possibly corrupt module?"); | ||
| 958 | goto stmLoadError; | ||
| 959 | } | ||
| 960 | |||
| 961 | fixSample(s); | ||
| 962 | } | ||
| 963 | } | ||
| 964 | |||
| 965 | fclose(f); | ||
| 966 | |||
| 967 | moduleLoaded = true; | ||
| 968 | return true; | ||
| 969 | |||
| 970 | stmLoadError: | ||
| 971 | fclose(f); | ||
| 972 | freeTmpModule(); | ||
| 973 | moduleFailedToLoad = true; | ||
| 974 | return false; | ||
| 975 | } | ||
| 976 | |||
| 977 | static int8_t countS3MChannels(uint16_t antPtn) | ||
| 978 | { | 49 | { |
| 979 | int32_t channels = 0; | 50 | "xm", "ft", "nst", "stk", "mod", "s3m", "stm", "fst", "digi", |
| 980 | for (int32_t i = 0; i < antPtn; i++) | ||
| 981 | { | ||
| 982 | if (pattTmp[i] == NULL) | ||
| 983 | continue; | ||
| 984 | 51 | ||
| 985 | tonTyp *ton = pattTmp[i]; | 52 | "END_OF_LIST" // do NOT move, remove or edit this line! |
| 986 | for (int32_t j = 0; j < 64; j++) | 53 | }; |
| 987 | { | ||
| 988 | for (int32_t k = 0; k < MAX_VOICES; k++, ton++) | ||
| 989 | { | ||
| 990 | if (ton->eff == 0 && ton->effTyp == 0 && ton->instr == 0 && ton->ton == 0 && ton->vol == 0) | ||
| 991 | continue; | ||
| 992 | 54 | ||
| 993 | if (k > channels) | 55 | // globals for module loaders |
| 994 | channels = k; | 56 | volatile bool tmpLinearPeriodsFlag; |
| 995 | } | 57 | int16_t pattLensTmp[MAX_PATTERNS]; |
| 996 | } | 58 | tonTyp *pattTmp[MAX_PATTERNS]; |
| 997 | } | 59 | instrTyp *instrTmp[1+256]; |
| 998 | channels++; | 60 | songTyp songTmp; |
| 61 | // -------------------------- | ||
| 999 | 62 | ||
| 1000 | return (int8_t)channels; | 63 | static volatile bool musicIsLoading, moduleLoaded, moduleFailedToLoad; |
| 1001 | } | 64 | static SDL_Thread *thread; |
| 65 | static uint8_t oldPlayMode; | ||
| 66 | static void setupLoadedModule(void); | ||
| 67 | static void freeTmpModule(void); | ||
| 1002 | 68 | ||
| 1003 | static bool loadMusicS3M(FILE *f, uint32_t dataLength, bool externalThreadFlag) | 69 | // Crude module detection routine. These aren't always accurate detections! |
| 70 | static int8_t detectModule(FILE *f) | ||
| 1004 | { | 71 | { |
| 1005 | int8_t *tmpSmp; | 72 | uint8_t D[256], I[4]; |
| 1006 | uint8_t ha[2048]; | ||
| 1007 | uint8_t alastnfo[32], alastefx[32], alastvibnfo[32], s3mLastGInstr[32]; | ||
| 1008 | uint8_t typ; | ||
| 1009 | int16_t ai, ap, ver, ii, kk, tmp; | ||
| 1010 | uint16_t ptnOfs[256]; | ||
| 1011 | int32_t i, j, k, len; | ||
| 1012 | tonTyp ton; | ||
| 1013 | sampleTyp *s; | ||
| 1014 | songS3MHeaderTyp h_S3M; | ||
| 1015 | songS3MinstrHeaderTyp h_S3MInstr; | ||
| 1016 | int16_t (*showMsg)(int16_t, const char *, const char *); | ||
| 1017 | |||
| 1018 | showMsg = externalThreadFlag ? okBoxThreadSafe : okBox; | ||
| 1019 | 73 | ||
| 74 | fseek(f, 0, SEEK_END); | ||
| 75 | uint32_t fileLength = (uint32_t)ftell(f); | ||
| 1020 | rewind(f); | 76 | rewind(f); |
| 1021 | 77 | ||
| 1022 | // start loading S3M | 78 | memset(D, 0, sizeof (D)); |
| 1023 | 79 | fread(D, 1, sizeof (D), f); | |
| 1024 | if (fread(&h_S3M, 1, sizeof (h_S3M), f) != sizeof (h_S3M)) | 80 | fseek(f, 1080, SEEK_SET); // MOD ID |
| 1025 | return loadMusicSTM(f, dataLength, externalThreadFlag); // not a .s3m, try loading as .stm | 81 | I[0] = I[1] = I[2] = I[3] = 0; |
| 1026 | 82 | fread(I, 1, 4, f); | |
| 1027 | if (memcmp(h_S3M.id, "SCRM", 4)) | 83 | rewind(f); |
| 1028 | return loadMusicSTM(f, dataLength, externalThreadFlag); // not a .s3m, try loading as .stm | ||
| 1029 | 84 | ||
| 1030 | loadedFormat = FORMAT_S3M; | 85 | // DIGI Booster (non-Pro) |
| 86 | if (!memcmp("DIGI Booster module", &D[0x00], 19+1) && D[0x19] >= 1 && D[0x19] <= 8) | ||
| 87 | return FORMAT_DIGI; | ||
| 1031 | 88 | ||
| 1032 | if (h_S3M.antInstr > MAX_INST || h_S3M.songTabLen > 256 || h_S3M.antPatt > 256 || | 89 | // Scream Tracker 3 S3M (and compatible trackers) |
| 1033 | h_S3M.typ != 16 || h_S3M.ver < 1 || h_S3M.ver > 2) | 90 | if (!memcmp("SCRM", &D[0x2C], 4) && D[0x1D] == 16) // XXX: byte=16 in all cases? |
| 1034 | { | 91 | return FORMAT_S3M; |
| 1035 | showMsg(0, "System message", "Error loading .s3m: Incompatible module!"); | ||
| 1036 | goto s3mLoadError; | ||
| 1037 | } | ||
| 1038 | 92 | ||
| 1039 | memset(songTmp.songTab, 255, sizeof (songTmp.songTab)); | 93 | // Scream Tracker 2 STM |
| 1040 | if (fread(songTmp.songTab, h_S3M.songTabLen, 1, f) != 1) | 94 | if ((!memcmp("!Scream!", &D[0x14], 8) || !memcmp("BMOD2STM", &D[0x14], 8) || |
| 95 | !memcmp("WUZAMOD!", &D[0x14], 8) || !memcmp("SWavePro", &D[0x14], 8)) && D[0x1D] == 2) // XXX: byte=2 for "WUZAMOD!"/"SWavePro" ? | ||
| 1041 | { | 96 | { |
| 1042 | showMsg(0, "System message", "General I/O error during loading! Is the file in use?"); | 97 | return FORMAT_STM; |
| 1043 | goto s3mLoadError; | ||
| 1044 | } | 98 | } |
| 1045 | 99 | ||
| 1046 | songTmp.len = h_S3M.songTabLen; | 100 | // Generic multi-channel MOD (1..9 channels) |
| 1047 | 101 | if (isdigit(I[0]) && I[0] != '0' && I[1] == 'C' && I[2] == 'H' && I[3] == 'N') // xCHN | |
| 1048 | // remove pattern separators (254) | 102 | return FORMAT_MOD; |
| 1049 | k = 0; | ||
| 1050 | j = 0; | ||
| 1051 | for (i = 0; i < songTmp.len; i++) | ||
| 1052 | { | ||
| 1053 | if (songTmp.songTab[i] != 254) | ||
| 1054 | songTmp.songTab[j++] = songTmp.songTab[i]; | ||
| 1055 | else | ||
| 1056 | k++; | ||
| 1057 | } | ||
| 1058 | 103 | ||
| 1059 | if (k <= songTmp.len) | 104 | // Generic multi-channel MOD (10..99 channels) |
| 1060 | songTmp.len -= (uint16_t)k; | 105 | if (isdigit(I[0]) && isdigit(I[1]) && I[0] != '0' && I[2] == 'C' && I[3] == 'H') // xxCH |
| 1061 | else | 106 | return FORMAT_MOD; |
| 1062 | songTmp.len = 1; | ||
| 1063 | 107 | ||
| 1064 | for (i = 1; i < songTmp.len; i++) | 108 | // Generic multi-channel MOD (10..99 channels) |
| 1065 | { | 109 | if (isdigit(I[0]) && isdigit(I[1]) && I[0] != '0' && I[2] == 'C' && I[3] == 'N') // xxCN (same as xxCH) |
| 1066 | if (songTmp.songTab[i] == 255) | 110 | return FORMAT_MOD; |
| 1067 | { | ||
| 1068 | songTmp.len = (uint16_t)i; | ||
| 1069 | break; | ||
| 1070 | } | ||
| 1071 | } | ||
| 1072 | 111 | ||
| 1073 | // clear unused song table entries | 112 | // Generic 4-channel MOD (NoiseTracker/ProTracker or compatible) |
| 1074 | if (songTmp.len < 255) | 113 | if (!memcmp("M.K.", I, 4) || !memcmp("M!K!", I, 4) || !memcmp("NSMS", I, 4) || |
| 1075 | memset(&songTmp.songTab[songTmp.len], 0, 255-songTmp.len); | 114 | !memcmp("LARD", I, 4) || !memcmp("PATT", I, 4) || !memcmp("PATT", I, 4) || |
| 1076 | 115 | !memcmp("FLT4", I, 4) || !memcmp("FLT8", I, 4) || !memcmp("N.T.", I, 4) || | |
| 1077 | songTmp.speed = h_S3M.defTempo; | 116 | !memcmp("M&K!", I, 4) || !memcmp("FEST", I, 4)) |
| 1078 | if (songTmp.speed < 32) | ||
| 1079 | songTmp.speed = 32; | ||
| 1080 | |||
| 1081 | songTmp.tempo = h_S3M.defSpeed; | ||
| 1082 | if (songTmp.tempo == 0) | ||
| 1083 | songTmp.tempo = 6; | ||
| 1084 | |||
| 1085 | if (songTmp.tempo > 31) | ||
| 1086 | songTmp.tempo = 31; | ||
| 1087 | |||
| 1088 | songTmp.initialTempo = songTmp.tempo; | ||
| 1089 | |||
| 1090 | // trim off spaces at end of name | ||
| 1091 | for (i = 19; i >= 0; i--) | ||
| 1092 | { | ||
| 1093 | if (h_S3M.name[i] == ' ' || h_S3M.name[i] == 0x1A) | ||
| 1094 | h_S3M.name[i] = '\0'; | ||
| 1095 | else | ||
| 1096 | break; | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | memcpy(songTmp.name, h_S3M.name, 20); | ||
| 1100 | songTmp.name[20] = '\0'; | ||
| 1101 | |||
| 1102 | ap = h_S3M.antPatt; | ||
| 1103 | ai = h_S3M.antInstr; | ||
| 1104 | ver = h_S3M.ver; | ||
| 1105 | |||
| 1106 | k = 31; | ||
| 1107 | while (k >= 0 && h_S3M.chanType[k] >= 16) k--; | ||
| 1108 | songTmp.antChn = (k + 2) & 254; | ||
| 1109 | |||
| 1110 | if (fread(ha, ai + ai, 1, f) != 1) | ||
| 1111 | { | ||
| 1112 | showMsg(0, "System message", "General I/O error during loading! Is the file in use?"); | ||
| 1113 | goto s3mLoadError; | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | if (fread(ptnOfs, ap + ap, 1, f) != 1) | ||
| 1117 | { | ||
| 1118 | showMsg(0, "System message", "General I/O error during loading! Is the file in use?"); | ||
| 1119 | goto s3mLoadError; | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | // *** PATTERNS *** | ||
| 1123 | |||
| 1124 | k = 0; | ||
| 1125 | for (i = 0; i < ap; i++) | ||
| 1126 | { | 117 | { |
| 1127 | if (ptnOfs[i] == 0) | 118 | return FORMAT_MOD; |
| 1128 | continue; // empty pattern | ||
| 1129 | |||
| 1130 | memset(alastnfo, 0, sizeof (alastnfo)); | ||
| 1131 | memset(alastefx, 0, sizeof (alastefx)); | ||
| 1132 | memset(alastvibnfo, 0, sizeof (alastvibnfo)); | ||
| 1133 | memset(s3mLastGInstr, 0, sizeof (s3mLastGInstr)); | ||
| 1134 | |||
| 1135 | fseek(f, ptnOfs[i] << 4, SEEK_SET); | ||
| 1136 | if (feof(f)) | ||
| 1137 | continue; | ||
| 1138 | |||
| 1139 | if (fread(&j, 2, 1, f) != 1) | ||
| 1140 | { | ||
| 1141 | showMsg(0, "System message", "General I/O error during loading! Is the file in use?"); | ||
| 1142 | goto s3mLoadError; | ||
| 1143 | } | ||
| 1144 | |||
| 1145 | if (j > 0 && j <= 12288) | ||
| 1146 | { | ||
| 1147 | pattTmp[i] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); | ||
| 1148 | if (pattTmp[i] == NULL) | ||
| 1149 | { | ||
| 1150 | showMsg(0, "System message", "Not enough memory!"); | ||
| 1151 | goto s3mLoadError; | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | pattLensTmp[i] = 64; | ||
| 1155 | fread(pattBuff, j, 1, f); | ||
| 1156 | |||
| 1157 | k = 0; | ||
| 1158 | kk = 0; | ||
| 1159 | |||
| 1160 | while (k < j && kk < 64) | ||
| 1161 | { | ||
| 1162 | typ = pattBuff[k++]; | ||
| 1163 | |||
| 1164 | if (typ == 0) | ||
| 1165 | { | ||
| 1166 | kk++; | ||
| 1167 | } | ||
| 1168 | else | ||
| 1169 | { | ||
| 1170 | ii = typ & 31; | ||
| 1171 | |||
| 1172 | memset(&ton, 0, sizeof (ton)); | ||
| 1173 | |||
| 1174 | // note and sample | ||
| 1175 | if (typ & 32) | ||
| 1176 | { | ||
| 1177 | ton.ton = pattBuff[k++]; | ||
| 1178 | ton.instr = pattBuff[k++]; | ||
| 1179 | |||
| 1180 | if (ton.instr > MAX_INST) | ||
| 1181 | ton.instr = 0; | ||
| 1182 | |||
| 1183 | if (ton.ton == 254) ton.ton = 97; | ||
| 1184 | else if (ton.ton == 255) ton.ton = 0; | ||
| 1185 | else | ||
| 1186 | { | ||
| 1187 | ton.ton = 1 + (ton.ton & 0xF) + (ton.ton >> 4) * 12; | ||
| 1188 | if (ton.ton > 96) | ||
| 1189 | ton.ton = 0; | ||
| 1190 | } | ||
| 1191 | } | ||
| 1192 | |||
| 1193 | // volume column | ||
| 1194 | if (typ & 64) | ||
| 1195 | { | ||
| 1196 | ton.vol = pattBuff[k++]; | ||
| 1197 | |||
| 1198 | if (ton.vol <= 64) | ||
| 1199 | ton.vol += 0x10; | ||
| 1200 | else | ||
| 1201 | ton.vol = 0; | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | // effect | ||
| 1205 | if (typ & 128) | ||
| 1206 | { | ||
| 1207 | ton.effTyp = pattBuff[k++]; | ||
| 1208 | ton.eff = pattBuff[k++]; | ||
| 1209 | |||
| 1210 | if (ton.eff > 0) | ||
| 1211 | { | ||
| 1212 | alastnfo[ii] = ton.eff; | ||
| 1213 | if (ton.effTyp == 8 || ton.effTyp == 21) | ||
| 1214 | alastvibnfo[ii] = ton.eff; // H/U | ||
| 1215 | } | ||
| 1216 | |||
| 1217 | // in ST3, a lot of effects directly share the same memory! | ||
| 1218 | if (ton.eff == 0 && ton.effTyp != 7) // G | ||
| 1219 | { | ||
| 1220 | uint8_t efx = ton.effTyp; | ||
| 1221 | if (efx == 8 || efx == 21) // H/U | ||
| 1222 | ton.eff = alastvibnfo[ii]; | ||
| 1223 | else if ((efx >= 4 && efx <= 12) || (efx >= 17 && efx <= 19)) // D/E/F/I/J/K/L/Q/R/S | ||
| 1224 | ton.eff = alastnfo[ii]; | ||
| 1225 | |||
| 1226 | /* If effect data is zero and effect type was the same as last one, clear out | ||
| 1227 | ** data if it's not J or S (those have no memory in the equivalent XM effects). | ||
| 1228 | ** Also goes for extra fine pitch slides and fine volume slides, | ||
| 1229 | ** since they get converted to other effects. | ||
| 1230 | */ | ||
| 1231 | if (efx == alastefx[ii] && ton.effTyp != 10 && ton.effTyp != 19) // J/S | ||
| 1232 | { | ||
| 1233 | uint8_t nfo = ton.eff; | ||
| 1234 | bool extraFinePitchSlides = (efx == 5 || efx == 6) && ((nfo & 0xF0) == 0xE0); | ||
| 1235 | bool fineVolSlides = (efx == 4 || efx == 11) && | ||
| 1236 | ((nfo > 0xF0) || (((nfo & 0xF) == 0xF) && ((nfo & 0xF0) > 0))); | ||
| 1237 | |||
| 1238 | if (!extraFinePitchSlides && !fineVolSlides) | ||
| 1239 | ton.eff = 0; | ||
| 1240 | } | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | if (ton.effTyp > 0) | ||
| 1244 | alastefx[ii] = ton.effTyp; | ||
| 1245 | |||
| 1246 | switch (ton.effTyp) | ||
| 1247 | { | ||
| 1248 | case 1: // A | ||
| 1249 | { | ||
| 1250 | ton.effTyp = 0xF; | ||
| 1251 | if (ton.eff == 0 || ton.eff > 0x1F) | ||
| 1252 | { | ||
| 1253 | ton.effTyp = 0; | ||
| 1254 | ton.eff = 0; | ||
| 1255 | } | ||
| 1256 | } | ||
| 1257 | break; | ||
| 1258 | |||
| 1259 | case 2: ton.effTyp = 0xB; break; // B | ||
| 1260 | case 3: ton.effTyp = 0xD; break; // C | ||
| 1261 | case 4: // D | ||
| 1262 | { | ||
| 1263 | if (ton.eff > 0xF0) // fine slide up | ||
| 1264 | { | ||
| 1265 | ton.effTyp = 0xE; | ||
| 1266 | ton.eff = 0xB0 | (ton.eff & 0xF); | ||
| 1267 | } | ||
| 1268 | else if ((ton.eff & 0x0F) == 0x0F && (ton.eff & 0xF0) > 0) // fine slide down | ||
| 1269 | { | ||
| 1270 | ton.effTyp = 0xE; | ||
| 1271 | ton.eff = 0xA0 | (ton.eff >> 4); | ||
| 1272 | } | ||
| 1273 | else | ||
| 1274 | { | ||
| 1275 | ton.effTyp = 0xA; | ||
| 1276 | if (ton.eff & 0x0F) // on D/K, last nybble has first priority in ST3 | ||
| 1277 | ton.eff &= 0x0F; | ||
| 1278 | } | ||
| 1279 | } | ||
| 1280 | break; | ||
| 1281 | |||
| 1282 | case 5: // E | ||
| 1283 | case 6: // F | ||
| 1284 | { | ||
| 1285 | if ((ton.eff & 0xF0) >= 0xE0) | ||
| 1286 | { | ||
| 1287 | // convert to fine slide | ||
| 1288 | if ((ton.eff & 0xF0) == 0xE0) | ||
| 1289 | tmp = 0x21; | ||
| 1290 | else | ||
| 1291 | tmp = 0xE; | ||
| 1292 | |||
| 1293 | ton.eff &= 0x0F; | ||
| 1294 | |||
| 1295 | if (ton.effTyp == 0x05) | ||
| 1296 | ton.eff |= 0x20; | ||
| 1297 | else | ||
| 1298 | ton.eff |= 0x10; | ||
| 1299 | |||
| 1300 | ton.effTyp = (uint8_t)tmp; | ||
| 1301 | |||
| 1302 | if (ton.effTyp == 0x21 && ton.eff == 0) | ||
| 1303 | { | ||
| 1304 | ton.effTyp = 0; | ||
| 1305 | } | ||
| 1306 | } | ||
| 1307 | else | ||
| 1308 | { | ||
| 1309 | // convert to normal 1xx/2xx slide | ||
| 1310 | ton.effTyp = 7 - ton.effTyp; | ||
| 1311 | } | ||
| 1312 | } | ||
| 1313 | break; | ||
| 1314 | |||
| 1315 | case 7: // G | ||
| 1316 | { | ||
| 1317 | ton.effTyp = 0x03; | ||
| 1318 | |||
| 1319 | // fix illegal slides (to new instruments) | ||
| 1320 | if (ton.instr != 0 && ton.instr != s3mLastGInstr[ii]) | ||
| 1321 | ton.instr = s3mLastGInstr[ii]; | ||
| 1322 | } | ||
| 1323 | break; | ||
| 1324 | |||
| 1325 | case 11: // K | ||
| 1326 | { | ||
| 1327 | if (ton.eff > 0xF0) // fine slide up | ||
| 1328 | { | ||
| 1329 | ton.effTyp = 0xE; | ||
| 1330 | ton.eff = 0xB0 | (ton.eff & 0xF); | ||
| 1331 | |||
| 1332 | // if volume column is unoccupied, set to vibrato | ||
| 1333 | if (ton.vol == 0) | ||
| 1334 | ton.vol = 0xB0; | ||
| 1335 | } | ||
| 1336 | else if ((ton.eff & 0x0F) == 0x0F && (ton.eff & 0xF0) > 0) // fine slide down | ||
| 1337 | { | ||
| 1338 | ton.effTyp = 0xE; | ||
| 1339 | ton.eff = 0xA0 | (ton.eff >> 4); | ||
| 1340 | |||
| 1341 | // if volume column is unoccupied, set to vibrato | ||
| 1342 | if (ton.vol == 0) | ||
| 1343 | ton.vol = 0xB0; | ||
| 1344 | } | ||
| 1345 | else | ||
| 1346 | { | ||
| 1347 | ton.effTyp = 0x6; | ||
| 1348 | if (ton.eff & 0x0F) // on D/K, last nybble has first priority in ST3 | ||
| 1349 | ton.eff &= 0x0F; | ||
| 1350 | } | ||
| 1351 | } | ||
| 1352 | break; | ||
| 1353 | |||
| 1354 | case 8: ton.effTyp = 0x04; break; // H | ||
| 1355 | case 9: ton.effTyp = 0x1D; break; // I | ||
| 1356 | case 10: ton.effTyp = 0x00; break; // J | ||
| 1357 | case 12: ton.effTyp = 0x05; break; // L | ||
| 1358 | case 15: ton.effTyp = 0x09; break; // O | ||
| 1359 | case 17: ton.effTyp = 0x1B; break; // Q | ||
| 1360 | case 18: ton.effTyp = 0x07; break; // R | ||
| 1361 | |||
| 1362 | case 19: // S | ||
| 1363 | { | ||
| 1364 | ton.effTyp = 0xE; | ||
| 1365 | tmp = ton.eff >> 4; | ||
| 1366 | ton.eff &= 0x0F; | ||
| 1367 | |||
| 1368 | if (tmp == 0x1) ton.eff |= 0x30; | ||
| 1369 | else if (tmp == 0x2) ton.eff |= 0x50; | ||
| 1370 | else if (tmp == 0x3) ton.eff |= 0x40; | ||
| 1371 | else if (tmp == 0x4) ton.eff |= 0x70; | ||
| 1372 | // ignore S8x becuase it's not compatible with FT2 panning | ||
| 1373 | else if (tmp == 0xB) ton.eff |= 0x60; | ||
| 1374 | else if (tmp == 0xC) // Note Cut | ||
| 1375 | { | ||
| 1376 | ton.eff |= 0xC0; | ||
| 1377 | if (ton.eff == 0xC0) | ||
| 1378 | { | ||
| 1379 | // EC0 does nothing in ST3 but cuts voice in FT2, remove effect | ||
| 1380 | ton.effTyp = 0; | ||
| 1381 | ton.eff = 0; | ||
| 1382 | } | ||
| 1383 | } | ||
| 1384 | else if (tmp == 0xD) // Note Delay | ||
| 1385 | { | ||
| 1386 | ton.eff |= 0xD0; | ||
| 1387 | if (ton.ton == 0 || ton.ton == 97) | ||
| 1388 | { | ||
| 1389 | // EDx without a note does nothing in ST3 but retrigs in FT2, remove effect | ||
| 1390 | ton.effTyp = 0; | ||
| 1391 | ton.eff = 0; | ||
| 1392 | } | ||
| 1393 | else if (ton.eff == 0xD0) | ||
| 1394 | { | ||
| 1395 | // ED0 prevents note/smp/vol from updating in ST3, remove everything | ||
| 1396 | ton.ton = 0; | ||
| 1397 | ton.instr = 0; | ||
| 1398 | ton.vol = 0; | ||
| 1399 | ton.effTyp = 0; | ||
| 1400 | ton.eff = 0; | ||
| 1401 | } | ||
| 1402 | } | ||
| 1403 | else if (tmp == 0xE) ton.eff |= 0xE0; | ||
| 1404 | else if (tmp == 0xF) ton.eff |= 0xF0; | ||
| 1405 | else | ||
| 1406 | { | ||
| 1407 | ton.effTyp = 0; | ||
| 1408 | ton.eff = 0; | ||
| 1409 | } | ||
| 1410 | } | ||
| 1411 | break; | ||
| 1412 | |||
| 1413 | case 20: // T | ||
| 1414 | { | ||
| 1415 | ton.effTyp = 0x0F; | ||
| 1416 | if (ton.eff < 0x21) // Txx with a value lower than 33 (0x21) does nothing in ST3, remove effect | ||
| 1417 | { | ||
| 1418 | ton.effTyp = 0; | ||
| 1419 | ton.eff = 0; | ||
| 1420 | } | ||
| 1421 | } | ||
| 1422 | break; | ||
| 1423 | |||
| 1424 | case 22: // V | ||
| 1425 | { | ||
| 1426 | ton.effTyp = 0x10; | ||
| 1427 | if (ton.eff > 0x40) | ||
| 1428 | { | ||
| 1429 | // Vxx > 0x40 does nothing in ST3 | ||
| 1430 | ton.effTyp = 0; | ||
| 1431 | ton.eff = 0; | ||
| 1432 | } | ||
| 1433 | } | ||
| 1434 | break; | ||
| 1435 | |||
| 1436 | default: | ||
| 1437 | { | ||
| 1438 | ton.effTyp = 0; | ||
| 1439 | ton.eff = 0; | ||
| 1440 | } | ||
| 1441 | break; | ||
| 1442 | } | ||
| 1443 | } | ||
| 1444 | |||
| 1445 | if (ton.instr != 0 && ton.effTyp != 0x3) | ||
| 1446 | s3mLastGInstr[ii] = ton.instr; | ||
| 1447 | |||
| 1448 | if (ton.effTyp > 35) | ||
| 1449 | { | ||
| 1450 | ton.effTyp = 0; | ||
| 1451 | ton.eff = 0; | ||
| 1452 | } | ||
| 1453 | |||
| 1454 | pattTmp[i][(kk * MAX_VOICES) + ii] = ton; | ||
| 1455 | } | ||
| 1456 | } | ||
| 1457 | |||
| 1458 | if (tmpPatternEmpty((uint16_t)i)) | ||
| 1459 | { | ||
| 1460 | if (pattTmp[i] != NULL) | ||
| 1461 | { | ||
| 1462 | free(pattTmp[i]); | ||
| 1463 | pattTmp[i] = NULL; | ||
| 1464 | } | ||
| 1465 | } | ||
| 1466 | } | ||
| 1467 | } | 119 | } |
| 1468 | 120 | ||
| 1469 | // *** SAMPLES *** | 121 | /* Check if the file is a .it module (Impulse Tracker, not supported). |
| 1470 | 122 | ** Some people may attempt to load .IT files in the FT2 clone, so | |
| 1471 | bool adlibInsWarn = false; | 123 | ** reject them here instead of accidentally loading them as .STK |
| 1472 | 124 | */ | |
| 1473 | memcpy(ptnOfs, ha, 512); | 125 | if (!memcmp("IMPM", D, 4) && D[0x16] == 0) |
| 1474 | for (i = 0; i < ai; i++) | 126 | return FORMAT_UNKNOWN; |
| 1475 | { | ||
| 1476 | fseek(f, ptnOfs[i] << 4, SEEK_SET); | ||
| 1477 | |||
| 1478 | if (fread(&h_S3MInstr, 1, sizeof (h_S3MInstr), f) != sizeof (h_S3MInstr)) | ||
| 1479 | { | ||
| 1480 | showMsg(0, "System message", "Not enough memory!"); | ||
| 1481 | goto s3mLoadError; | ||
| 1482 | } | ||
| 1483 | |||
| 1484 | // trim off spaces at end of name | ||
| 1485 | for (j = 21; j >= 0; j--) | ||
| 1486 | { | ||
| 1487 | if (h_S3MInstr.name[j] == ' ' || h_S3MInstr.name[j] == 0x1A) | ||
| 1488 | h_S3MInstr.name[j] = '\0'; | ||
| 1489 | else | ||
| 1490 | break; | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | memcpy(songTmp.instrName[1+i], h_S3MInstr.name, 22); | ||
| 1494 | songTmp.instrName[1+i][22] = '\0'; | ||
| 1495 | |||
| 1496 | if (h_S3MInstr.typ == 2) | ||
| 1497 | { | ||
| 1498 | adlibInsWarn = true; | ||
| 1499 | } | ||
| 1500 | else if (h_S3MInstr.typ == 1) | ||
| 1501 | { | ||
| 1502 | if ((h_S3MInstr.flags & (255-1-2-4)) != 0 || h_S3MInstr.pack != 0) | ||
| 1503 | { | ||
| 1504 | showMsg(0, "System message", "Error loading .s3m: Incompatible module!"); | ||
| 1505 | goto s3mLoadError; | ||
| 1506 | } | ||
| 1507 | else if (h_S3MInstr.memSeg > 0 && h_S3MInstr.len > 0) | ||
| 1508 | { | ||
| 1509 | if (!allocateTmpInstr((int16_t)(1 + i))) | ||
| 1510 | { | ||
| 1511 | showMsg(0, "System message", "Not enough memory!"); | ||
| 1512 | goto s3mLoadError; | ||
| 1513 | } | ||
| 1514 | |||
| 1515 | setNoEnvelope(instrTmp[1 + i]); | ||
| 1516 | s = &instrTmp[1+i]->samp[0]; | ||
| 1517 | |||
| 1518 | len = h_S3MInstr.len; | ||
| 1519 | |||
| 1520 | bool hasLoop = h_S3MInstr.flags & 1; | ||
| 1521 | bool stereoSample = (h_S3MInstr.flags >> 1) & 1; | ||
| 1522 | bool is16Bit = (h_S3MInstr.flags >> 2) & 1; | ||
| 1523 | |||
| 1524 | if (is16Bit) // 16-bit | ||
| 1525 | len <<= 1; | ||
| 1526 | |||
| 1527 | if (stereoSample) // stereo | ||
| 1528 | len <<= 1; | ||
| 1529 | |||
| 1530 | tmpSmp = (int8_t *)malloc(len + LOOP_FIX_LEN); | ||
| 1531 | if (tmpSmp == NULL) | ||
| 1532 | { | ||
| 1533 | showMsg(0, "System message", "Not enough memory!"); | ||
| 1534 | goto s3mLoadError; | ||
| 1535 | } | ||
| 1536 | |||
| 1537 | int8_t *newPtr = tmpSmp + SMP_DAT_OFFSET; | ||
| 1538 | |||
| 1539 | memcpy(s->name, h_S3MInstr.name, 21); | ||
| 1540 | |||
| 1541 | if (h_S3MInstr.c2Spd > 65535) // ST3 (and OpenMPT) does this | ||
| 1542 | h_S3MInstr.c2Spd = 65535; | ||
| 1543 | |||
| 1544 | tuneSample(s, h_S3MInstr.c2Spd, false); | ||
| 1545 | |||
| 1546 | s->len = h_S3MInstr.len; | ||
| 1547 | s->vol = h_S3MInstr.vol; | ||
| 1548 | s->repS = h_S3MInstr.repS; | ||
| 1549 | s->repL = h_S3MInstr.repE - h_S3MInstr.repS; | ||
| 1550 | |||
| 1551 | // non-FT2: fixes "miracle man.s3m" | ||
| 1552 | if ((h_S3MInstr.memSeg<<4)+s->len > (int32_t)dataLength) | ||
| 1553 | s->len = dataLength - (h_S3MInstr.memSeg << 4); | ||
| 1554 | |||
| 1555 | if (s->repL <= 2 || s->repS+s->repL > s->len) | ||
| 1556 | { | ||
| 1557 | s->repS = 0; | ||
| 1558 | s->repL = 0; | ||
| 1559 | hasLoop = false; | ||
| 1560 | } | ||
| 1561 | |||
| 1562 | if (s->repL == 0) | ||
| 1563 | hasLoop = false; | ||
| 1564 | |||
| 1565 | s->typ = hasLoop + (is16Bit << 4); | ||
| 1566 | |||
| 1567 | if (s->vol > 64) | ||
| 1568 | s->vol = 64; | ||
| 1569 | |||
| 1570 | s->pan = 128; | ||
| 1571 | |||
| 1572 | fseek(f, h_S3MInstr.memSeg << 4, SEEK_SET); | ||
| 1573 | |||
| 1574 | // non-FT2: fixes "miracle man.s3m" | ||
| 1575 | if ((h_S3MInstr.memSeg<<4)+len > (int32_t)dataLength) | ||
| 1576 | len = dataLength - (h_S3MInstr.memSeg << 4); | ||
| 1577 | |||
| 1578 | if (ver == 1) | ||
| 1579 | { | ||
| 1580 | fseek(f, len, SEEK_CUR); // sample not supported | ||
| 1581 | } | ||
| 1582 | else | ||
| 1583 | { | ||
| 1584 | if (fread(newPtr, len, 1, f) != 1) | ||
| 1585 | { | ||
| 1586 | free(tmpSmp); | ||
| 1587 | showMsg(0, "System message", "General I/O error during loading! Is the file in use?"); | ||
| 1588 | goto s3mLoadError; | ||
| 1589 | } | ||
| 1590 | |||
| 1591 | if (is16Bit) | ||
| 1592 | { | ||
| 1593 | conv16BitSample(newPtr, len, stereoSample); | ||
| 1594 | |||
| 1595 | s->origPek = tmpSmp; | ||
| 1596 | s->pek = s->origPek + SMP_DAT_OFFSET; | ||
| 1597 | |||
| 1598 | s->len <<= 1; | ||
| 1599 | s->repS <<= 1; | ||
| 1600 | s->repL <<= 1; | ||
| 1601 | } | ||
| 1602 | else | ||
| 1603 | { | ||
| 1604 | conv8BitSample(newPtr, len, stereoSample); | ||
| 1605 | |||
| 1606 | s->origPek = tmpSmp; | ||
| 1607 | s->pek = s->origPek + SMP_DAT_OFFSET; | ||
| 1608 | } | ||
| 1609 | |||
| 1610 | // if stereo sample: reduce memory footprint after sample was downmixed to mono | ||
| 1611 | if (stereoSample) | ||
| 1612 | { | ||
| 1613 | newPtr = (int8_t *)realloc(s->origPek, s->len + LOOP_FIX_LEN); | ||
| 1614 | if (newPtr != NULL) | ||
| 1615 | { | ||
| 1616 | s->origPek = newPtr; | ||
| 1617 | s->pek = s->origPek + SMP_DAT_OFFSET; | ||
| 1618 | } | ||
| 1619 | } | ||
| 1620 | |||
| 1621 | fixSample(s); | ||
| 1622 | } | ||
| 1623 | } | ||
| 1624 | } | ||
| 1625 | } | ||
| 1626 | 127 | ||
| 1627 | fclose(f); | 128 | /* Fasttracker II XM and compatible trackers. |
| 129 | ** Note: This test can falsely be true for STK modules (and non-supported files) where the | ||
| 130 | ** first 17 bytes start with "Extended Module: ". This is unlikely to happen. | ||
| 131 | */ | ||
| 132 | if (!memcmp("Extended Module: ", &D[0x00], 17)) | ||
| 133 | return FORMAT_XM; | ||
| 1628 | 134 | ||
| 1629 | songTmp.antChn = countS3MChannels(ap); | 135 | /* Lastly, we assume that the file is either a 15-sample STK or an unsupported file. |
| 136 | ** Let's assume it's an STK and do some sanity checks. If they fail, we have an | ||
| 137 | ** unsupported file. | ||
| 138 | */ | ||
| 1630 | 139 | ||
| 1631 | if (adlibInsWarn) | 140 | // minimum and maximum (?) possible size for a supported STK |
| 1632 | showMsg(0, "System message", "Warning: The module contains unsupported AdLib instruments!"); | 141 | if (fileLength < 1624 || fileLength > 984634) |
| 142 | return FORMAT_UNKNOWN; | ||
| 1633 | 143 | ||
| 1634 | if (!(config.dontShowAgainFlags & DONT_SHOW_S3M_LOAD_WARNING_FLAG)) | 144 | // test STK numOrders+BPM for illegal values |
| 1635 | showMsg(6, "System message", "Warning: S3M channel panning is ignored because it's not compatible with FT2."); | 145 | fseek(f, 470, SEEK_SET); |
| 146 | D[0] = D[1] = 0; | ||
| 147 | fread(D, 1, 2, f); | ||
| 148 | rewind(f); | ||
| 1636 | 149 | ||
| 1637 | moduleLoaded = true; | 150 | if (D[0] >= 0 && D[0] <= 128 && D[1] <= 220) |
| 1638 | return true; | 151 | return FORMAT_POSSIBLY_STK; |
| 1639 | 152 | ||
| 1640 | s3mLoadError: | 153 | return FORMAT_UNKNOWN; |
| 1641 | fclose(f); | ||
| 1642 | freeTmpModule(); | ||
| 1643 | moduleFailedToLoad = true; | ||
| 1644 | return false; | ||
| 1645 | } | 154 | } |
| 1646 | 155 | ||
| 1647 | bool doLoadMusic(bool externalThreadFlag) | 156 | static bool doLoadMusic(bool externalThreadFlag) |
| 1648 | { | 157 | { |
| 1649 | char tmpText[128]; | 158 | // setup message box functions |
| 1650 | int16_t k; | 159 | loaderMsgBox = externalThreadFlag ? myLoaderMsgBoxThreadSafe : myLoaderMsgBox; |
| 1651 | uint16_t i; | 160 | loaderSysReq = externalThreadFlag ? okBoxThreadSafe : okBox; |
| 1652 | uint32_t filelength; | ||
| 1653 | songHeaderTyp h; | ||
| 1654 | FILE *f; | ||
| 1655 | int16_t (*showMsg)(int16_t, const char *, const char *); | ||
| 1656 | |||
| 1657 | showMsg = externalThreadFlag ? okBoxThreadSafe : okBox; | ||
| 1658 | |||
| 1659 | linearFreqTable = false; | ||
| 1660 | 161 | ||
| 1661 | if (editor.tmpFilenameU == NULL) | 162 | if (editor.tmpFilenameU == NULL) |
| 1662 | { | 163 | { |
| 1663 | showMsg(0, "System message", "Generic memory fault during loading!"); | 164 | loaderMsgBox("Generic memory fault during loading!"); |
| 1664 | moduleFailedToLoad = true; | 165 | goto loadError; |
| 1665 | return false; | ||
| 1666 | } | 166 | } |
| 1667 | 167 | ||
| 1668 | f = UNICHAR_FOPEN(editor.tmpFilenameU, "rb"); | 168 | FILE *f = UNICHAR_FOPEN(editor.tmpFilenameU, "rb"); |
| 1669 | if (f == NULL) | 169 | if (f == NULL) |
| 1670 | { | 170 | { |
| 1671 | showMsg(0, "System message", "General I/O error during loading! Is the file in use? Does it exist?"); | 171 | loaderMsgBox("General I/O error during loading! Is the file in use? Does it exist?"); |
| 1672 | moduleFailedToLoad = true; | 172 | goto loadError; |
| 1673 | return false; | ||
| 1674 | } | 173 | } |
| 1675 | 174 | ||
| 175 | int8_t format = detectModule(f); | ||
| 1676 | fseek(f, 0, SEEK_END); | 176 | fseek(f, 0, SEEK_END); |
| 1677 | filelength = ftell(f); | 177 | uint32_t filesize = ftell(f); |
| 1678 | rewind(f); | ||
| 1679 | |||
| 1680 | // start loading | ||
| 1681 | if (fread(&h, 1, sizeof (h), f) != sizeof (h)) | ||
| 1682 | return loadMusicS3M(f, filelength, externalThreadFlag); // not a .xm file, try to load as .s3m | ||
| 1683 | |||
| 1684 | if (memcmp(h.sig, "Extended Module: ", 17)) | ||
| 1685 | return loadMusicS3M(f, filelength, externalThreadFlag); // not a .xm file, try to load as .s3m | ||
| 1686 | |||
| 1687 | loadedFormat = FORMAT_XM; | ||
| 1688 | |||
| 1689 | if (h.ver < 0x0102 || h.ver > 0x0104) | ||
| 1690 | { | ||
| 1691 | fclose(f); | ||
| 1692 | |||
| 1693 | const int32_t major = (h.ver >> 8) & 0x0F; | ||
| 1694 | const int32_t minor = h.ver & 0xFF; | ||
| 1695 | |||
| 1696 | sprintf(tmpText, "Error loading XM: Unsupported file version (v%01X.%02X).", major, minor); | ||
| 1697 | showMsg(0, "System message", tmpText); | ||
| 1698 | |||
| 1699 | moduleFailedToLoad = true; | ||
| 1700 | return false; | ||
| 1701 | } | ||
| 1702 | |||
| 1703 | if (h.len > MAX_ORDERS) | ||
| 1704 | { | ||
| 1705 | showMsg(0, "System message", "Error loading XM: The song has more than 256 orders!"); | ||
| 1706 | goto xmLoadError; | ||
| 1707 | } | ||
| 1708 | |||
| 1709 | if (h.antPtn > MAX_PATTERNS) | ||
| 1710 | { | ||
| 1711 | showMsg(0, "System message", "Error loading XM: The song has more than 256 patterns!"); | ||
| 1712 | goto xmLoadError; | ||
| 1713 | } | ||
| 1714 | |||
| 1715 | if (h.antChn == 0) | ||
| 1716 | { | ||
| 1717 | showMsg(0, "System message", "Error loading XM: This file is corrupt."); | ||
| 1718 | goto xmLoadError; | ||
| 1719 | } | ||
| 1720 | |||
| 1721 | if (h.antInstrs > 256) // if >128 instruments, we fake-load up to 128 extra instruments and discard them | ||
| 1722 | { | ||
| 1723 | showMsg(0, "System message", "Error loading XM: This file is corrupt."); | ||
| 1724 | goto xmLoadError; | ||
| 1725 | } | ||
| 1726 | |||
| 1727 | fseek(f, 60 + h.headerSize, SEEK_SET); | ||
| 1728 | if (filelength != 336 && feof(f)) // 336 in length at this point = empty XM | ||
| 1729 | { | ||
| 1730 | showMsg(0, "System message", "Error loading XM: The module is empty!"); | ||
| 1731 | goto xmLoadError; | ||
| 1732 | } | ||
| 1733 | |||
| 1734 | // trim off spaces at end of name | ||
| 1735 | for (k = 19; k >= 0; k--) | ||
| 1736 | { | ||
| 1737 | if (h.name[k] == ' ' || h.name[k] == 0x1A) | ||
| 1738 | h.name[k] = '\0'; | ||
| 1739 | else | ||
| 1740 | break; | ||
| 1741 | } | ||
| 1742 | |||
| 1743 | memcpy(songTmp.name, h.name, 20); | ||
| 1744 | songTmp.name[20] = '\0'; | ||
| 1745 | 178 | ||
| 1746 | songTmp.len = h.len; | 179 | rewind(f); |
| 1747 | songTmp.repS = h.repS; | 180 | switch (format) |
| 1748 | songTmp.antChn = (uint8_t)h.antChn; | ||
| 1749 | songTmp.speed = h.defSpeed; | ||
| 1750 | songTmp.tempo = h.defTempo; | ||
| 1751 | songTmp.ver = h.ver; | ||
| 1752 | linearFreqTable = h.flags & 1; | ||
| 1753 | |||
| 1754 | songTmp.speed = CLAMP(songTmp.speed, 1, 999); | ||
| 1755 | songTmp.tempo = CLAMP(songTmp.tempo, 1, 99); | ||
| 1756 | |||
| 1757 | songTmp.initialTempo = songTmp.tempo; | ||
| 1758 | |||
| 1759 | if (songTmp.globVol > 64) | ||
| 1760 | songTmp.globVol = 64; | ||
| 1761 | |||
| 1762 | if (songTmp.len == 0) | ||
| 1763 | songTmp.len = 1; // songTmp.songTab is already empty | ||
| 1764 | else | ||
| 1765 | memcpy(songTmp.songTab, h.songTab, songTmp.len); | ||
| 1766 | |||
| 1767 | // some strange XMs have the order list padded with 0xFF, remove them! | ||
| 1768 | for (int16_t j = 255; j >= 0; j--) | ||
| 1769 | { | ||
| 1770 | if (songTmp.songTab[j] != 0xFF) | ||
| 1771 | break; | ||
| 1772 | |||
| 1773 | if (songTmp.len > j) | ||
| 1774 | songTmp.len = j; | ||
| 1775 | } | ||
| 1776 | |||
| 1777 | // even though XM supports 256 orders, FT2 supports only 255... | ||
| 1778 | if (songTmp.len > 0xFF) | ||
| 1779 | songTmp.len = 0xFF; | ||
| 1780 | |||
| 1781 | if (songTmp.ver < 0x0104) | ||
| 1782 | { | ||
| 1783 | // XM v1.02 and XM v1.03 | ||
| 1784 | |||
| 1785 | for (i = 1; i <= h.antInstrs; i++) | ||
| 1786 | { | ||
| 1787 | if (!loadInstrHeader(f, i, externalThreadFlag)) | ||
| 1788 | goto xmLoadError; | ||
| 1789 | } | ||
| 1790 | |||
| 1791 | if (!loadPatterns(f, h.antPtn, externalThreadFlag)) | ||
| 1792 | goto xmLoadError; | ||
| 1793 | |||
| 1794 | for (i = 1; i <= h.antInstrs; i++) | ||
| 1795 | { | ||
| 1796 | if (!loadInstrSample(f, i, externalThreadFlag)) | ||
| 1797 | goto xmLoadError; | ||
| 1798 | } | ||
| 1799 | } | ||
| 1800 | else | ||
| 1801 | { | 181 | { |
| 1802 | // XM v1.04 (latest version) | 182 | case FORMAT_XM: moduleLoaded = loadXM(f, filesize); break; |
| 1803 | 183 | case FORMAT_S3M: moduleLoaded = loadS3M(f, filesize); break; | |
| 1804 | if (!loadPatterns(f, h.antPtn, externalThreadFlag)) | 184 | case FORMAT_STM: moduleLoaded = loadSTM(f, filesize); break; |
| 1805 | goto xmLoadError; | 185 | case FORMAT_MOD: moduleLoaded = loadMOD(f, filesize); break; |
| 186 | case FORMAT_POSSIBLY_STK: moduleLoaded = loadSTK(f, filesize); break; | ||
| 187 | case FORMAT_DIGI: moduleLoaded = loadDIGI(f, filesize); break; | ||
| 1806 | 188 | ||
| 1807 | for (i = 1; i <= h.antInstrs; i++) | 189 | default: |
| 1808 | { | 190 | loaderMsgBox("This file is not a supported module!"); |
| 1809 | if (!loadInstrHeader(f, i, externalThreadFlag)) | 191 | break; |
| 1810 | goto xmLoadError; | ||
| 1811 | |||
| 1812 | if (!loadInstrSample(f, i, externalThreadFlag)) | ||
| 1813 | goto xmLoadError; | ||
| 1814 | } | ||
| 1815 | } | ||
| 1816 | |||
| 1817 | // if we temporarily loaded more than 128 instruments, clear the extra allocated memory | ||
| 1818 | if (h.antInstrs > MAX_INST) | ||
| 1819 | { | ||
| 1820 | for (i = MAX_INST+1; i <= h.antInstrs; i++) | ||
| 1821 | { | ||
| 1822 | if (instrTmp[i] != NULL) | ||
| 1823 | { | ||
| 1824 | free(instrTmp[i]); | ||
| 1825 | instrTmp[i] = NULL; | ||
| 1826 | } | ||
| 1827 | } | ||
| 1828 | } | 192 | } |
| 1829 | |||
| 1830 | fclose(f); | 193 | fclose(f); |
| 1831 | 194 | ||
| 1832 | /* We support loading XMs with up to 32 samples per instrument (ModPlug/OpenMPT), | 195 | if (!moduleLoaded) |
| 1833 | ** but only the first 16 will be loaded. Now make sure we set the number of samples | 196 | goto loadError; |
| 1834 | ** back to max 16 in the headers before loading is done. | ||
| 1835 | */ | ||
| 1836 | bool instrHasMoreThan16Samples = false; | ||
| 1837 | for (i = 1; i <= MAX_INST; i++) | ||
| 1838 | { | ||
| 1839 | if (instrTmp[i] != NULL && instrTmp[i]->antSamp > MAX_SMP_PER_INST) | ||
| 1840 | { | ||
| 1841 | instrHasMoreThan16Samples = true; | ||
| 1842 | instrTmp[i]->antSamp = MAX_SMP_PER_INST; | ||
| 1843 | } | ||
| 1844 | } | ||
| 1845 | |||
| 1846 | if (songTmp.antChn > MAX_VOICES) | ||
| 1847 | { | ||
| 1848 | songTmp.antChn = MAX_VOICES; | ||
| 1849 | showMsg(0, "System message", "Warning: This XM contains >32 channels. The extra channels will be discarded!"); | ||
| 1850 | } | ||
| 1851 | |||
| 1852 | if (h.antInstrs > MAX_INST) | ||
| 1853 | showMsg(0, "System message", "Warning: This XM contains >128 instruments. The extra instruments will be discarded!"); | ||
| 1854 | |||
| 1855 | if (instrHasMoreThan16Samples) | ||
| 1856 | showMsg(0, "System message", "Warning: This XM contains instrument(s) with >16 samples. The extra samples will be discarded!"); | ||
| 1857 | 197 | ||
| 1858 | moduleLoaded = true; | 198 | moduleLoaded = true; |
| 1859 | return true; | 199 | return true; |
| 1860 | 200 | ||
| 1861 | xmLoadError: | 201 | loadError: |
| 1862 | fclose(f); | ||
| 1863 | freeTmpModule(); | 202 | freeTmpModule(); |
| 1864 | moduleFailedToLoad = true; | 203 | moduleFailedToLoad = true; |
| 1865 | return false; | 204 | return false; |
| 1866 | } | 205 | } |
| 1867 | 206 | ||
| 207 | static void clearTmpModule(void) | ||
| 208 | { | ||
| 209 | memset(pattTmp, 0, sizeof (pattTmp)); | ||
| 210 | memset(instrTmp, 0, sizeof (instrTmp)); | ||
| 211 | memset(&songTmp, 0, sizeof (songTmp)); | ||
| 212 | |||
| 213 | for (uint32_t i = 0; i < MAX_PATTERNS; i++) | ||
| 214 | pattLensTmp[i] = 64; | ||
| 215 | } | ||
| 216 | |||
| 1868 | static int32_t SDLCALL loadMusicThread(void *ptr) | 217 | static int32_t SDLCALL loadMusicThread(void *ptr) |
| 1869 | { | 218 | { |
| 1870 | return doLoadMusic(true); | 219 | return doLoadMusic(true); |
| 1871 | |||
| 1872 | (void)ptr; | 220 | (void)ptr; |
| 1873 | } | 221 | } |
| 1874 | 222 | ||
| 1875 | void loadMusic(UNICHAR *filenameU) | 223 | void loadMusic(UNICHAR *filenameU) |
| 1876 | { | 224 | { |
| 1877 | if (musicIsLoading) | 225 | if (musicIsLoading || filenameU == NULL) |
| 1878 | return; | 226 | return; |
| 1879 | 227 | ||
| 1880 | mouseAnimOn(); | 228 | mouseAnimOn(); |
| @@ -1882,19 +230,9 @@ void loadMusic(UNICHAR *filenameU) · 1 unmodified line | |||
| 1882 | musicIsLoading = true; | 230 | musicIsLoading = true; |
| 1883 | moduleLoaded = false; | 231 | moduleLoaded = false; |
| 1884 | moduleFailedToLoad = false; | 232 | moduleFailedToLoad = false; |
| 1885 | loadedFormat = FORMAT_NONE; | ||
| 1886 | 233 | ||
| 1887 | UNICHAR_STRCPY(editor.tmpFilenameU, filenameU); | 234 | clearTmpModule(); // clear stuff from last loading session (very important) |
| 1888 | 235 | UNICHAR_STRCPY(editor.tmpFilenameU, filenameU); | |
| 1889 | // clear deprecated pointers from possible last loading session (super important) | ||
| 1890 | memset(pattTmp, 0, sizeof (pattTmp)); | ||
| 1891 | memset(instrTmp, 0, sizeof (instrTmp)); | ||
| 1892 | |||
| 1893 | // prevent stuck instrument names from previous module | ||
| 1894 | memset(&songTmp, 0, sizeof (songTmp)); | ||
| 1895 | |||
| 1896 | for (uint32_t i = 0; i < MAX_PATTERNS; i++) | ||
| 1897 | pattLensTmp[i] = 64; | ||
| 1898 | 236 | ||
| 1899 | thread = SDL_CreateThread(loadMusicThread, NULL, NULL); | 237 | thread = SDL_CreateThread(loadMusicThread, NULL, NULL); |
| 1900 | if (thread == NULL) | 238 | if (thread == NULL) |
| @@ -1910,19 +248,10 @@ void loadMusic(UNICHAR *filenameU) · 9 unmodified lines | |||
| 1910 | 248 | ||
| 1911 | bool loadMusicUnthreaded(UNICHAR *filenameU, bool autoPlay) | 249 | bool loadMusicUnthreaded(UNICHAR *filenameU, bool autoPlay) |
| 1912 | { | 250 | { |
| 1913 | if (filenameU == NULL || editor.tmpFilenameU == NULL) | 251 | if (filenameU == NULL) |
| 1914 | return false; | 252 | return false; |
| 1915 | 253 | ||
| 1916 | // clear deprecated pointers from possible last loading session (super important) | 254 | clearTmpModule(); // clear stuff from last loading session (very important) |
| 1917 | memset(pattTmp, 0, sizeof (pattTmp)); | ||
| 1918 | memset(instrTmp, 0, sizeof (instrTmp)); | ||
| 1919 | |||
| 1920 | // prevent stuck instrument names from previous module | ||
| 1921 | memset(&songTmp, 0, sizeof (songTmp)); | ||
| 1922 | |||
| 1923 | for (uint32_t i = 0; i < MAX_PATTERNS; i++) | ||
| 1924 | pattLensTmp[i] = 64; | ||
| 1925 | |||
| 1926 | UNICHAR_STRCPY(editor.tmpFilenameU, filenameU); | 255 | UNICHAR_STRCPY(editor.tmpFilenameU, filenameU); |
| 1927 | 256 | ||
| 1928 | editor.loadMusicEvent = EVENT_NONE; | 257 | editor.loadMusicEvent = EVENT_NONE; |
| @@ -1940,6 +269,36 @@ bool loadMusicUnthreaded(UNICHAR *filenameU, bool autoPlay) · 11 unmodified lines | |||
| 1940 | return false; | 269 | return false; |
| 1941 | } | 270 | } |
| 1942 | 271 | ||
| 272 | bool allocateTmpPatt(int32_t nr, uint16_t rows) | ||
| 273 | { | ||
| 274 | pattTmp[nr] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); | ||
| 275 | if (pattTmp[nr] == NULL) | ||
| 276 | return false; | ||
| 277 | |||
| 278 | pattLensTmp[nr] = rows; | ||
| 279 | return true; | ||
| 280 | } | ||
| 281 | |||
| 282 | bool allocateTmpInstr(int16_t nr) | ||
| 283 | { | ||
| 284 | if (instrTmp[nr] != NULL) | ||
| 285 | return false; // already allocated | ||
| 286 | |||
| 287 | instrTyp *p = (instrTyp *)calloc(1, sizeof (instrTyp)); | ||
| 288 | if (p == NULL) | ||
| 289 | return false; | ||
| 290 | |||
| 291 | sampleTyp *s = p->samp; | ||
| 292 | for (int32_t i = 0; i < MAX_SMP_PER_INST; i++, s++) | ||
| 293 | { | ||
| 294 | s->pan = 128; | ||
| 295 | s->vol = 64; | ||
| 296 | } | ||
| 297 | |||
| 298 | instrTmp[nr] = p; | ||
| 299 | return true; | ||
| 300 | } | ||
| 301 | |||
| 1943 | static void freeTmpModule(void) // called on module load error | 302 | static void freeTmpModule(void) // called on module load error |
| 1944 | { | 303 | { |
| 1945 | // free all patterns | 304 | // free all patterns |
| @@ -1970,404 +329,7 @@ static void freeTmpModule(void) // called on module load error · 24 unmodified lines | |||
| 1970 | } | 329 | } |
| 1971 | } | 330 | } |
| 1972 | 331 | ||
| 1973 | static bool loadInstrHeader(FILE *f, uint16_t i, bool externalThreadFlag) | 332 | bool tmpPatternEmpty(uint16_t nr) |
| 1974 | { | ||
| 1975 | int8_t k; | ||
| 1976 | uint8_t j; | ||
| 1977 | uint32_t readSize; | ||
| 1978 | instrHeaderTyp ih; | ||
| 1979 | instrTyp *ins; | ||
| 1980 | sampleHeaderTyp *src; | ||
| 1981 | sampleTyp *s; | ||
| 1982 | int16_t (*showMsg)(int16_t, const char *, const char *); | ||
| 1983 | |||
| 1984 | showMsg = externalThreadFlag ? okBoxThreadSafe : okBox; | ||
| 1985 | |||
| 1986 | memset(extraSampleLengths, 0, sizeof (extraSampleLengths)); | ||
| 1987 | memset(&ih, 0, sizeof (ih)); | ||
| 1988 | fread(&ih.instrSize, 4, 1, f); | ||
| 1989 | |||
| 1990 | readSize = ih.instrSize; | ||
| 1991 | |||
| 1992 | // yes, some XMs can have a header size of 0, and it usually means 263 bytes (INSTR_HEADER_SIZE) | ||
| 1993 | if (readSize == 0 || readSize > INSTR_HEADER_SIZE) | ||
| 1994 | readSize = INSTR_HEADER_SIZE; | ||
| 1995 | |||
| 1996 | if (readSize < 4) | ||
| 1997 | { | ||
| 1998 | showMsg(0, "System message", "Error loading XM: This file is corrupt (or not supported)!"); | ||
| 1999 | return false; | ||
| 2000 | } | ||
| 2001 | |||
| 2002 | // load instrument data into temp buffer | ||
| 2003 | fread(ih.name, readSize-4, 1, f); // -4 = skip ih.instrSize | ||
| 2004 | |||
| 2005 | // FT2 bugfix: skip instrument header data if instrSize is above INSTR_HEADER_SIZE | ||
| 2006 | if (ih.instrSize > INSTR_HEADER_SIZE) | ||
| 2007 | fseek(f, ih.instrSize-INSTR_HEADER_SIZE, SEEK_CUR); | ||
| 2008 | |||
| 2009 | if (ih.antSamp < 0 || ih.antSamp > 32) | ||
| 2010 | { | ||
| 2011 | showMsg(0, "System message", "Error loading XM: This file is corrupt (or not supported)!"); | ||
| 2012 | return false; | ||
| 2013 | } | ||
| 2014 | |||
| 2015 | if (i <= MAX_INST) // copy over instrument names | ||
| 2016 | { | ||
| 2017 | // trim off spaces at end of name | ||
| 2018 | for (k = 21; k >= 0; k--) | ||
| 2019 | { | ||
| 2020 | if (ih.name[k] == ' ' || ih.name[k] == 0x1A) | ||
| 2021 | ih.name[k] = '\0'; | ||
| 2022 | else | ||
| 2023 | break; | ||
| 2024 | } | ||
| 2025 | |||
| 2026 | memcpy(songTmp.instrName[i], ih.name, 22); | ||
| 2027 | songTmp.instrName[i][22] = '\0'; | ||
| 2028 | } | ||
| 2029 | |||
| 2030 | if (ih.antSamp > 0) | ||
| 2031 | { | ||
| 2032 | if (!allocateTmpInstr(i)) | ||
| 2033 | { | ||
| 2034 | showMsg(0, "System message", "Not enough memory!"); | ||
| 2035 | return false; | ||
| 2036 | } | ||
| 2037 | |||
| 2038 | // copy instrument header elements to our instrument struct | ||
| 2039 | |||
| 2040 | ins = instrTmp[i]; | ||
| 2041 | memcpy(ins->ta, ih.ta, 96); | ||
| 2042 | memcpy(ins->envVP, ih.envVP, 12*2*sizeof(int16_t)); | ||
| 2043 | memcpy(ins->envPP, ih.envPP, 12*2*sizeof(int16_t)); | ||
| 2044 | ins->envVPAnt = ih.envVPAnt; | ||
| 2045 | ins->envPPAnt = ih.envPPAnt; | ||
| 2046 | ins->envVSust = ih.envVSust; | ||
| 2047 | ins->envVRepS = ih.envVRepS; | ||
| 2048 | ins->envVRepE = ih.envVRepE; | ||
| 2049 | ins->envPSust = ih.envPSust; | ||
| 2050 | ins->envPRepS = ih.envPRepS; | ||
| 2051 | ins->envPRepE = ih.envPRepE; | ||
| 2052 | ins->envVTyp = ih.envVTyp; | ||
| 2053 | ins->envPTyp = ih.envPTyp; | ||
| 2054 | ins->vibTyp = ih.vibTyp; | ||
| 2055 | ins->vibSweep = ih.vibSweep; | ||
| 2056 | ins->vibDepth = ih.vibDepth; | ||
| 2057 | ins->vibRate = ih.vibRate; | ||
| 2058 | ins->fadeOut = ih.fadeOut; | ||
| 2059 | ins->midiOn = (ih.midiOn == 1) ? true : false; | ||
| 2060 | ins->midiChannel = ih.midiChannel; | ||
| 2061 | ins->midiProgram = ih.midiProgram; | ||
| 2062 | ins->midiBend = ih.midiBend; | ||
| 2063 | ins->mute = (ih.mute == 1) ? true : false; | ||
| 2064 | ins->antSamp = ih.antSamp; // used in loadInstrSample() | ||
| 2065 | |||
| 2066 | // sanitize stuff for broken/unsupported instruments | ||
| 2067 | ins->midiProgram = CLAMP(ins->midiProgram, 0, 127); | ||
| 2068 | ins->midiBend = CLAMP(ins->midiBend, 0, 36); | ||
| 2069 | |||
| 2070 | if (ins->midiChannel > 15) ins->midiChannel = 15; | ||
| 2071 | if (ins->vibDepth > 0x0F) ins->vibDepth = 0x0F; | ||
| 2072 | if (ins->vibRate > 0x3F) ins->vibRate = 0x3F; | ||
| 2073 | if (ins->vibTyp > 3) ins->vibTyp = 0; | ||
| 2074 | |||
| 2075 | for (j = 0; j < 96; j++) | ||
| 2076 | { | ||
| 2077 | if (ins->ta[j] >= MAX_SMP_PER_INST) | ||
| 2078 | ins->ta[j] = MAX_SMP_PER_INST-1; | ||
| 2079 | } | ||
| 2080 | |||
| 2081 | if (ins->envVPAnt > 12) ins->envVPAnt = 12; | ||
| 2082 | if (ins->envVRepS > 11) ins->envVRepS = 11; | ||
| 2083 | if (ins->envVRepE > 11) ins->envVRepE = 11; | ||
| 2084 | if (ins->envVSust > 11) ins->envVSust = 11; | ||
| 2085 | if (ins->envPPAnt > 12) ins->envPPAnt = 12; | ||
| 2086 | if (ins->envPRepS > 11) ins->envPRepS = 11; | ||
| 2087 | if (ins->envPRepE > 11) ins->envPRepE = 11; | ||
| 2088 | if (ins->envPSust > 11) ins->envPSust = 11; | ||
| 2089 | |||
| 2090 | for (j = 0; j < 12; j++) | ||
| 2091 | { | ||
| 2092 | if ((uint16_t)ins->envVP[j][0] > 32767) ins->envVP[j][0] = 32767; | ||
| 2093 | if ((uint16_t)ins->envPP[j][0] > 32767) ins->envPP[j][0] = 32767; | ||
| 2094 | if ((uint16_t)ins->envVP[j][1] > 64) ins->envVP[j][1] = 64; | ||
| 2095 | if ((uint16_t)ins->envPP[j][1] > 63) ins->envPP[j][1] = 63; | ||
| 2096 | } | ||
| 2097 | |||
| 2098 | int32_t sampleHeadersToRead = ih.antSamp; | ||
| 2099 | if (sampleHeadersToRead > MAX_SMP_PER_INST) | ||
| 2100 | sampleHeadersToRead = MAX_SMP_PER_INST; | ||
| 2101 | |||
| 2102 | if (fread(ih.samp, sampleHeadersToRead * sizeof (sampleHeaderTyp), 1, f) != 1) | ||
| 2103 | { | ||
| 2104 | showMsg(0, "System message", "General I/O error during loading!"); | ||
| 2105 | return false; | ||
| 2106 | } | ||
| 2107 | |||
| 2108 | // if instrument contains more than 16 sample headers (unsupported), skip them | ||
| 2109 | if (ih.antSamp > MAX_SMP_PER_INST) // can only be 0..32 at this point | ||
| 2110 | { | ||
| 2111 | const int32_t samplesToSkip = ih.antSamp-MAX_SMP_PER_INST; | ||
| 2112 | for (j = 0; j < samplesToSkip; j++) | ||
| 2113 | { | ||
| 2114 | fread(&extraSampleLengths[j], 4, 1, f); // used for skipping data in loadInstrSample() | ||
| 2115 | fseek(f, sizeof (sampleHeaderTyp)-4, SEEK_CUR); | ||
| 2116 | } | ||
| 2117 | } | ||
| 2118 | |||
| 2119 | for (j = 0; j < sampleHeadersToRead; j++) | ||
| 2120 | { | ||
| 2121 | s = &instrTmp[i]->samp[j]; | ||
| 2122 | src = &ih.samp[j]; | ||
| 2123 | |||
| 2124 | // copy sample header elements to our sample struct | ||
| 2125 | |||
| 2126 | s->len = src->len; | ||
| 2127 | s->repS = src->repS; | ||
| 2128 | s->repL = src->repL; | ||
| 2129 | s->vol = src->vol; | ||
| 2130 | s->fine = src->fine; | ||
| 2131 | s->typ = src->typ; | ||
| 2132 | s->pan = src->pan; | ||
| 2133 | s->relTon = src->relTon; | ||
| 2134 | memcpy(s->name, src->name, 22); | ||
| 2135 | s->name[22] = '\0'; | ||
| 2136 | |||
| 2137 | // dst->pek is set up later | ||
| 2138 | |||
| 2139 | // trim off spaces at end of name | ||
| 2140 | for (k = 21; k >= 0; k--) | ||
| 2141 | { | ||
| 2142 | if (s->name[k] == ' ' || s->name[k] == 0x1A) | ||
| 2143 | s->name[k] = '\0'; | ||
| 2144 | else | ||
| 2145 | break; | ||
| 2146 | } | ||
| 2147 | |||
| 2148 | // sanitize stuff broken/unsupported samples | ||
| 2149 | if (s->vol > 64) | ||
| 2150 | s->vol = 64; | ||
| 2151 | |||
| 2152 | s->relTon = CLAMP(s->relTon, -48, 71); | ||
| 2153 | } | ||
| 2154 | } | ||
| 2155 | |||
| 2156 | return true; | ||
| 2157 | } | ||
| 2158 | |||
| 2159 | void checkSampleRepeat(sampleTyp *s) | ||
| 2160 | { | ||
| 2161 | if (s->repS < 0) s->repS = 0; | ||
| 2162 | if (s->repL < 0) s->repL = 0; | ||
| 2163 | if (s->repS > s->len) s->repS = s->len; | ||
| 2164 | if (s->repS+s->repL > s->len) s->repL = s->len - s->repS; | ||
| 2165 | if (s->repL == 0) s->typ &= ~3; // non-FT2 fix: force loop off if looplen is 0 | ||
| 2166 | } | ||
| 2167 | |||
| 2168 | static bool loadInstrSample(FILE *f, uint16_t i, bool externalThreadFlag) | ||
| 2169 | { | ||
| 2170 | int16_t (*showMsg)(int16_t, const char *, const char *); | ||
| 2171 | |||
| 2172 | showMsg = externalThreadFlag ? okBoxThreadSafe : okBox; | ||
| 2173 | |||
| 2174 | if (instrTmp[i] == NULL) | ||
| 2175 | return true; // empty instrument, let's just pretend it got loaded successfully | ||
| 2176 | |||
| 2177 | uint16_t k = instrTmp[i]->antSamp; | ||
| 2178 | if (k > MAX_SMP_PER_INST) | ||
| 2179 | k = MAX_SMP_PER_INST; | ||
| 2180 | |||
| 2181 | sampleTyp *s = instrTmp[i]->samp; | ||
| 2182 | |||
| 2183 | if (i > MAX_INST) // insNum > 128, just skip sample data | ||
| 2184 | { | ||
| 2185 | for (uint16_t j = 0; j < k; j++, s++) | ||
| 2186 | { | ||
| 2187 | if (s->len > 0) | ||
| 2188 | fseek(f, s->len, SEEK_CUR); | ||
| 2189 | } | ||
| 2190 | } | ||
| 2191 | else | ||
| 2192 | { | ||
| 2193 | for (uint16_t j = 0; j < k; j++, s++) | ||
| 2194 | { | ||
| 2195 | // if a sample has both forward loop and pingpong loop set, make it pingpong loop only (FT2 mixer behavior) | ||
| 2196 | if ((s->typ & 3) == 3) | ||
| 2197 | s->typ &= 0xFE; | ||
| 2198 | |||
| 2199 | int32_t l = s->len; | ||
| 2200 | if (l <= 0) | ||
| 2201 | { | ||
| 2202 | s->pek = NULL; | ||
| 2203 | s->len = 0; | ||
| 2204 | s->repL = 0; | ||
| 2205 | s->repS = 0; | ||
| 2206 | |||
| 2207 | if (s->typ & 32) | ||
| 2208 | s->typ &= ~32; // remove stereo flag | ||
| 2209 | } | ||
| 2210 | else | ||
| 2211 | { | ||
| 2212 | int32_t bytesToSkip = 0; | ||
| 2213 | if (l > MAX_SAMPLE_LEN) | ||
| 2214 | { | ||
| 2215 | bytesToSkip = l - MAX_SAMPLE_LEN; | ||
| 2216 | l = MAX_SAMPLE_LEN; | ||
| 2217 | } | ||
| 2218 | |||
| 2219 | s->pek = NULL; | ||
| 2220 | s->origPek = (int8_t *)malloc(l + LOOP_FIX_LEN); | ||
| 2221 | if (s->origPek == NULL) | ||
| 2222 | { | ||
| 2223 | showMsg(0, "System message", "Not enough memory!"); | ||
| 2224 | return false; | ||
| 2225 | } | ||
| 2226 | |||
| 2227 | s->pek = s->origPek + SMP_DAT_OFFSET; | ||
| 2228 | |||
| 2229 | const int32_t bytesRead = (int32_t)fread(s->pek, 1, l, f); | ||
| 2230 | if (bytesRead < l) | ||
| 2231 | { | ||
| 2232 | const int32_t bytesToClear = l - bytesRead; | ||
| 2233 | memset(&s->pek[bytesRead], 0, bytesToClear); | ||
| 2234 | } | ||
| 2235 | |||
| 2236 | if (bytesToSkip > 0) | ||
| 2237 | fseek(f, bytesToSkip, SEEK_CUR); | ||
| 2238 | |||
| 2239 | delta2Samp(s->pek, l, s->typ); | ||
| 2240 | |||
| 2241 | if (s->typ & 32) // stereo sample - already downmixed to mono in delta2samp() | ||
| 2242 | { | ||
| 2243 | s->typ &= ~32; // remove stereo flag | ||
| 2244 | |||
| 2245 | s->len >>= 1; | ||
| 2246 | s->repL >>= 1; | ||
| 2247 | s->repS >>= 1; | ||
| 2248 | |||
| 2249 | int8_t *newPtr = (int8_t *)realloc(s->origPek, s->len + LOOP_FIX_LEN); | ||
| 2250 | if (newPtr != NULL) | ||
| 2251 | { | ||
| 2252 | s->origPek = newPtr; | ||
| 2253 | s->pek = s->origPek + SMP_DAT_OFFSET; | ||
| 2254 | } | ||
| 2255 | } | ||
| 2256 | } | ||
| 2257 | |||
| 2258 | // NON-FT2 FIX: Align to 2-byte if 16-bit sample | ||
| 2259 | if (s->typ & 16) | ||
| 2260 | { | ||
| 2261 | s->repL &= 0xFFFFFFFE; | ||
| 2262 | s->repS &= 0xFFFFFFFE; | ||
| 2263 | s->len &= 0xFFFFFFFE; | ||
| 2264 | } | ||
| 2265 | |||
| 2266 | checkSampleRepeat(s); | ||
| 2267 | fixSample(s); | ||
| 2268 | } | ||
| 2269 | } | ||
| 2270 | |||
| 2271 | if (instrTmp[i]->antSamp > MAX_SMP_PER_INST) | ||
| 2272 | { | ||
| 2273 | const int32_t samplesToSkip = instrTmp[i]->antSamp-MAX_SMP_PER_INST; | ||
| 2274 | for (i = 0; i < samplesToSkip; i++) | ||
| 2275 | { | ||
| 2276 | if (extraSampleLengths[i] > 0) | ||
| 2277 | fseek(f, extraSampleLengths[i], SEEK_CUR); | ||
| 2278 | } | ||
| 2279 | } | ||
| 2280 | |||
| 2281 | return true; | ||
| 2282 | } | ||
| 2283 | |||
| 2284 | void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn) | ||
| 2285 | { | ||
| 2286 | int32_t j; | ||
| 2287 | |||
| 2288 | if (dst == NULL) | ||
| 2289 | return; | ||
| 2290 | |||
| 2291 | const int32_t srcEnd = len * (sizeof (tonTyp) * antChn); | ||
| 2292 | int32_t srcIdx = 0; | ||
| 2293 | |||
| 2294 | int32_t numChannels = antChn; | ||
| 2295 | if (numChannels > MAX_VOICES) | ||
| 2296 | numChannels = MAX_VOICES; | ||
| 2297 | |||
| 2298 | const int32_t pitch = sizeof (tonTyp) * (MAX_VOICES - antChn); | ||
| 2299 | for (int32_t i = 0; i < len; i++) | ||
| 2300 | { | ||
| 2301 | for (j = 0; j < numChannels; j++) | ||
| 2302 | { | ||
| 2303 | if (srcIdx >= srcEnd) | ||
| 2304 | return; // error! | ||
| 2305 | |||
| 2306 | const uint8_t note = *src++; | ||
| 2307 | if (note & 0x80) | ||
| 2308 | { | ||
| 2309 | *dst++ = (note & 0x01) ? *src++ : 0; | ||
| 2310 | *dst++ = (note & 0x02) ? *src++ : 0; | ||
| 2311 | *dst++ = (note & 0x04) ? *src++ : 0; | ||
| 2312 | *dst++ = (note & 0x08) ? *src++ : 0; | ||
| 2313 | *dst++ = (note & 0x10) ? *src++ : 0; | ||
| 2314 | } | ||
| 2315 | else | ||
| 2316 | { | ||
| 2317 | *dst++ = note; | ||
| 2318 | *dst++ = *src++; | ||
| 2319 | *dst++ = *src++; | ||
| 2320 | *dst++ = *src++; | ||
| 2321 | *dst++ = *src++; | ||
| 2322 | } | ||
| 2323 | |||
| 2324 | // if note is overflowing (>97), remove it | ||
| 2325 | if (*(dst-5) > 97) | ||
| 2326 | *(dst-5) = 0; | ||
| 2327 | |||
| 2328 | // non-FT2 security fix: if effect is above 35 (Z), clear effect and parameter | ||
| 2329 | if (*(dst-2) > 35) | ||
| 2330 | { | ||
| 2331 | *(dst-2) = 0; | ||
| 2332 | *(dst-1) = 0; | ||
| 2333 | } | ||
| 2334 | |||
| 2335 | srcIdx += sizeof (tonTyp); | ||
| 2336 | } | ||
| 2337 | |||
| 2338 | // if more than 32 channels, skip rest of the channels for this row | ||
| 2339 | for (; j < antChn; j++) | ||
| 2340 | { | ||
| 2341 | if (srcIdx >= srcEnd) | ||
| 2342 | return; // error! | ||
| 2343 | |||
| 2344 | const uint8_t note = *src++; | ||
| 2345 | if (note & 0x80) | ||
| 2346 | { | ||
| 2347 | if (note & 0x01) src++; | ||
| 2348 | if (note & 0x02) src++; | ||
| 2349 | if (note & 0x04) src++; | ||
| 2350 | if (note & 0x08) src++; | ||
| 2351 | if (note & 0x10) src++; | ||
| 2352 | } | ||
| 2353 | else | ||
| 2354 | { | ||
| 2355 | src++; | ||
| 2356 | src++; | ||
| 2357 | src++; | ||
| 2358 | src++; | ||
| 2359 | } | ||
| 2360 | |||
| 2361 | srcIdx += sizeof (tonTyp); | ||
| 2362 | } | ||
| 2363 | |||
| 2364 | // if song has <32 channels, align pointer to next row (skip unused channels) | ||
| 2365 | if (antChn < MAX_VOICES) | ||
| 2366 | dst += pitch; | ||
| 2367 | } | ||
| 2368 | } | ||
| 2369 | |||
| 2370 | static bool tmpPatternEmpty(uint16_t nr) | ||
| 2371 | { | 333 | { |
| 2372 | if (pattTmp[nr] == NULL) | 334 | if (pattTmp[nr] == NULL) |
| 2373 | return true; | 335 | return true; |
| @@ -2396,97 +358,6 @@ void clearUnusedChannels(tonTyp *p, int16_t pattLen, int32_t antChn) · 22 unmodified lines | |||
| 2396 | memset(ptr, 0, width); | 358 | memset(ptr, 0, width); |
| 2397 | } | 359 | } |
| 2398 | 360 | ||
| 2399 | static bool loadPatterns(FILE *f, uint16_t antPtn, bool externalThreadFlag) | ||
| 2400 | { | ||
| 2401 | uint8_t tmpLen; | ||
| 2402 | patternHeaderTyp ph; | ||
| 2403 | int16_t (*showMsg)(int16_t, const char *, const char *); | ||
| 2404 | |||
| 2405 | showMsg = externalThreadFlag ? okBoxThreadSafe : okBox; | ||
| 2406 | |||
| 2407 | bool pattLenWarn = false; | ||
| 2408 | for (uint16_t i = 0; i < antPtn; i++) | ||
| 2409 | { | ||
| 2410 | if (fread(&ph.patternHeaderSize, 4, 1, f) != 1) | ||
| 2411 | goto pattCorrupt; | ||
| 2412 | |||
| 2413 | if (fread(&ph.typ, 1, 1, f) != 1) | ||
| 2414 | goto pattCorrupt; | ||
| 2415 | |||
| 2416 | ph.pattLen = 0; | ||
| 2417 | if (songTmp.ver == 0x0102) | ||
| 2418 | { | ||
| 2419 | if (fread(&tmpLen, 1, 1, f) != 1) | ||
| 2420 | goto pattCorrupt; | ||
| 2421 | |||
| 2422 | if (fread(&ph.dataLen, 2, 1, f) != 1) | ||
| 2423 | goto pattCorrupt; | ||
| 2424 | |||
| 2425 | ph.pattLen = tmpLen + 1; // +1 in v1.02 | ||
| 2426 | |||
| 2427 | if (ph.patternHeaderSize > 8) | ||
| 2428 | fseek(f, ph.patternHeaderSize - 8, SEEK_CUR); | ||
| 2429 | } | ||
| 2430 | else | ||
| 2431 | { | ||
| 2432 | if (fread(&ph.pattLen, 2, 1, f) != 1) | ||
| 2433 | goto pattCorrupt; | ||
| 2434 | |||
| 2435 | if (fread(&ph.dataLen, 2, 1, f) != 1) | ||
| 2436 | goto pattCorrupt; | ||
| 2437 | |||
| 2438 | if (ph.patternHeaderSize > 9) | ||
| 2439 | fseek(f, ph.patternHeaderSize - 9, SEEK_CUR); | ||
| 2440 | } | ||
| 2441 | |||
| 2442 | if (feof(f)) | ||
| 2443 | goto pattCorrupt; | ||
| 2444 | |||
| 2445 | pattLensTmp[i] = ph.pattLen; | ||
| 2446 | if (pattLensTmp[i] > MAX_PATT_LEN) | ||
| 2447 | { | ||
| 2448 | pattLensTmp[i] = MAX_PATT_LEN; | ||
| 2449 | pattLenWarn = true; | ||
| 2450 | } | ||
| 2451 | |||
| 2452 | if (ph.dataLen > 0) | ||
| 2453 | { | ||
| 2454 | pattTmp[i] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); | ||
| 2455 | if (pattTmp[i] == NULL) | ||
| 2456 | { | ||
| 2457 | showMsg(0, "System message", "Not enough memory!"); | ||
| 2458 | return false; | ||
| 2459 | } | ||
| 2460 | |||
| 2461 | if (fread(packedPattData, 1, ph.dataLen, f) != ph.dataLen) | ||
| 2462 | goto pattCorrupt; | ||
| 2463 | |||
| 2464 | unpackPatt((uint8_t *)pattTmp[i], packedPattData, pattLensTmp[i], songTmp.antChn); | ||
| 2465 | clearUnusedChannels(pattTmp[i], pattLensTmp[i], songTmp.antChn); | ||
| 2466 | } | ||
| 2467 | |||
| 2468 | if (tmpPatternEmpty(i)) | ||
| 2469 | { | ||
| 2470 | if (pattTmp[i] != NULL) | ||
| 2471 | { | ||
| 2472 | free(pattTmp[i]); | ||
| 2473 | pattTmp[i] = NULL; | ||
| 2474 | } | ||
| 2475 | |||
| 2476 | pattLensTmp[i] = 64; | ||
| 2477 | } | ||
| 2478 | } | ||
| 2479 | |||
| 2480 | if (pattLenWarn) | ||
| 2481 | showMsg(0, "System message", "This module contains pattern(s) with a length above 256! They will be truncated."); | ||
| 2482 | |||
| 2483 | return true; | ||
| 2484 | |||
| 2485 | pattCorrupt: | ||
| 2486 | showMsg(0, "System message", "Error loading XM: This file is corrupt!"); | ||
| 2487 | return false; | ||
| 2488 | } | ||
| 2489 | |||
| 2490 | // called from input/video thread after the module was done loading | 361 | // called from input/video thread after the module was done loading |
| 2491 | static void setupLoadedModule(void) | 362 | static void setupLoadedModule(void) |
| 2492 | { | 363 | { |
| @@ -2513,16 +384,28 @@ static void setupLoadedModule(void) · 20 unmodified lines | |||
| 2513 | pattLens[i] = pattLensTmp[i]; | 384 | pattLens[i] = pattLensTmp[i]; |
| 2514 | } | 385 | } |
| 2515 | 386 | ||
| 387 | // copy over song struct | ||
| 388 | memcpy(&song, &songTmp, sizeof (songTyp)); | ||
| 389 | fixSongName(); | ||
| 390 | |||
| 2516 | // copy over new instruments (includes sample pointers) | 391 | // copy over new instruments (includes sample pointers) |
| 2517 | for (int16_t i = 1; i <= MAX_INST; i++) | 392 | for (int16_t i = 1; i <= MAX_INST; i++) |
| 2518 | { | 393 | { |
| 2519 | instr[i] = instrTmp[i]; | 394 | instr[i] = instrTmp[i]; |
| 2520 | fixSampleName(i); | 395 | fixInstrAndSampleNames(i); |
| 2521 | } | ||
| 2522 | 396 | ||
| 2523 | // copy over song struct | 397 | if (instr[i] != NULL) |
| 2524 | memcpy(&song, &songTmp, sizeof (songTyp)); | 398 | { |
| 2525 | fixSongName(); | 399 | for (int32_t j = 0; j < MAX_SMP_PER_INST; j++) |
| 400 | { | ||
| 401 | sampleTyp *s = &instr[i]->samp[j]; | ||
| 402 | |||
| 403 | checkSampleRepeat(s); | ||
| 404 | if (s->pek != NULL) | ||
| 405 | fixSample(s); // prepare sample for branchless linear interpolation | ||
| 406 | } | ||
| 407 | } | ||
| 408 | } | ||
| 2526 | 409 | ||
| 2527 | // we are the owners of the allocated memory ptrs set by the loader thread now | 410 | // we are the owners of the allocated memory ptrs set by the loader thread now |
| 2528 | 411 | ||
| @@ -2555,10 +438,7 @@ static void setupLoadedModule(void) · 26 unmodified lines | |||
| 2555 | editor.timer = song.timer; | 438 | editor.timer = song.timer; |
| 2556 | editor.globalVol = song.globVol; | 439 | editor.globalVol = song.globVol; |
| 2557 | 440 | ||
| 2558 | if (loadedFormat == FORMAT_XM) | 441 | setFrqTab(tmpLinearPeriodsFlag); |
| 2559 | setFrqTab(linearFreqTable); | ||
| 2560 | else | ||
| 2561 | setFrqTab(false); | ||
| 2562 | 442 | ||
| 2563 | unlockMixerCallback(); | 443 | unlockMixerCallback(); |
| 2564 | 444 | ||
| @@ -2639,14 +519,9 @@ bool handleModuleLoadFromArg(int argc, char **argv) · 74 unmodified lines | |||
| 2639 | UNICHAR_CHDIR(editor.binaryPathU); | 519 | UNICHAR_CHDIR(editor.binaryPathU); |
| 2640 | 520 | ||
| 2641 | const int32_t filesize = getFileSize(filenameU); | 521 | const int32_t filesize = getFileSize(filenameU); |
| 2642 | if (filesize == -1 || filesize >= 512L*1024*1024) // >=2GB or >=512MB | 522 | if (filesize == -1 || filesize >= 512L*1024*1024) // 1) >=2GB 2) >=512MB |
| 2643 | { | 523 | { |
| 2644 | okBox(0, "System message", "Error: The module is too big to be loaded!"); | 524 | okBox(0, "System message", "Error: The module is too big to be loaded!"); |
| 2645 | /* This is not really true, but let's add this check to prevent accidentally | ||
| 2646 | ** passing really big files to the program. And how often do you really | ||
| 2647 | ** see a >=512MB .XM/.S3M module? | ||
| 2648 | */ | ||
| 2649 | |||
| 2650 | free(filenameU); | 525 | free(filenameU); |
| 2651 | UNICHAR_CHDIR(tmpPathU); // set old path back | 526 | UNICHAR_CHDIR(tmpPathU); // set old path back |
| 2652 | return false; | 527 | return false; |
| @@ -2659,6 +534,64 @@ bool handleModuleLoadFromArg(int argc, char **argv) · 6 unmodified lines | |||
| 2659 | return result; | 534 | return result; |
| 2660 | } | 535 | } |
| 2661 | 536 | ||
| 537 | static bool fileIsModule(UNICHAR *pathU) | ||
| 538 | { | ||
| 539 | FILE *f = UNICHAR_FOPEN(pathU, "rb"); | ||
| 540 | if (f == NULL) | ||
| 541 | return false; | ||
| 542 | |||
| 543 | int8_t modFormat = detectModule(f); | ||
| 544 | fclose(f); | ||
| 545 | |||
| 546 | /* If the module was not identified (possibly STK type), | ||
| 547 | ** check the file extension and handle it as a module only | ||
| 548 | ** if it starts with "mod."/"stk." or ends with ".mod"/".stk" (case insensitive). | ||
| 549 | */ | ||
| 550 | if (modFormat == FORMAT_POSSIBLY_STK) | ||
| 551 | { | ||
| 552 | char *path = unicharToCp437(pathU, false); | ||
| 553 | if (path == NULL) | ||
| 554 | return false; | ||
| 555 | |||
| 556 | int32_t pathLen = (int32_t)strlen(path); | ||
| 557 | |||
| 558 | // get filename from path | ||
| 559 | int32_t i = pathLen; | ||
| 560 | while (i--) | ||
| 561 | { | ||
| 562 | if (path[i] == DIR_DELIMITER) | ||
| 563 | break; | ||
| 564 | } | ||
| 565 | |||
| 566 | char *filename = path; | ||
| 567 | if (i > 0) | ||
| 568 | filename += i + 1; | ||
| 569 | |||
| 570 | int32_t filenameLen = (int32_t)strlen(filename); | ||
| 571 | // -------------------------- | ||
| 572 | |||
| 573 | if (filenameLen > 5) | ||
| 574 | { | ||
| 575 | if (!_strnicmp("mod.", filename, 4) || !_strnicmp("stk.", filename, 4)) | ||
| 576 | { | ||
| 577 | free(path); | ||
| 578 | return true; | ||
| 579 | } | ||
| 580 | |||
| 581 | if (!_strnicmp(".mod", &filename[filenameLen-4], 4) || !_strnicmp(".stk", &filename[filenameLen-4], 4)) | ||
| 582 | { | ||
| 583 | free(path); | ||
| 584 | return true; | ||
| 585 | } | ||
| 586 | } | ||
| 587 | |||
| 588 | free(path); | ||
| 589 | return false; | ||
| 590 | } | ||
| 591 | |||
| 592 | return (modFormat != FORMAT_UNKNOWN); | ||
| 593 | } | ||
| 594 | |||
| 2662 | void loadDroppedFile(char *fullPathUTF8, bool songModifiedCheck) | 595 | void loadDroppedFile(char *fullPathUTF8, bool songModifiedCheck) |
| 2663 | { | 596 | { |
| 2664 | if (ui.sysReqShown || fullPathUTF8 == NULL) | 597 | if (ui.sysReqShown || fullPathUTF8 == NULL) |
| @@ -2701,15 +634,11 @@ void loadDroppedFile(char *fullPathUTF8, bool songModifiedCheck) · 36 unmodified lines | |||
| 2701 | 634 | ||
| 2702 | // pass UTF8 to these tests so that we can test file ending in ASCII/ANSI | 635 | // pass UTF8 to these tests so that we can test file ending in ASCII/ANSI |
| 2703 | 636 | ||
| 2704 | if (fileIsInstrument(fullPathUTF8)) | 637 | if (fileIsInstr(fullPathU)) |
| 2705 | { | 638 | { |
| 2706 | loadInstr(fullPathU); | 639 | loadInstr(fullPathU); |
| 2707 | } | 640 | } |
| 2708 | else if (fileIsSample(fullPathUTF8)) | 641 | else if (fileIsModule(fullPathU)) |
| 2709 | { | ||
| 2710 | loadSample(fullPathU, editor.curSmp, false); | ||
| 2711 | } | ||
| 2712 | else | ||
| 2713 | { | 642 | { |
| 2714 | SDL_RestoreWindow(video.window); | 643 | SDL_RestoreWindow(video.window); |
| 2715 | 644 | ||
| @@ -2729,6 +658,10 @@ void loadDroppedFile(char *fullPathUTF8, bool songModifiedCheck) · 13 unmodified lines | |||
| 2729 | editor.loadMusicEvent = EVENT_LOADMUSIC_DRAGNDROP; | 658 | editor.loadMusicEvent = EVENT_LOADMUSIC_DRAGNDROP; |
| 2730 | loadMusic(fullPathU); | 659 | loadMusic(fullPathU); |
| 2731 | } | 660 | } |
| 661 | else | ||
| 662 | { | ||
| 663 | loadSample(fullPathU, editor.curSmp, false); | ||
| 664 | } | ||
| 2732 | 665 | ||
| 2733 | free(fullPathU); | 666 | free(fullPathU); |
| 2734 | } | 667 | } |