ft2_diskop.c (56973B)
1 // for finding memory leaks in debug mode with Visual Studio 2 #if defined _DEBUG && defined _MSC_VER 3 #include <crtdbg.h> 4 #endif 5 6 #define _FILE_OFFSET_BITS 64 7 8 #include <stdint.h> 9 #include <stdio.h> 10 #include <math.h> 11 #ifdef _WIN32 12 #define WIN32_MEAN_AND_LEAN 13 #include <shlwapi.h> 14 #include <windows.h> 15 #include <direct.h> 16 #include <shlobj.h> // SHGetFolderPathW() 17 #else 18 #include <sys/types.h> 19 #include <sys/stat.h> 20 #include <fts.h> // for fts_open() and stuff in recursiveDelete() 21 #include <unistd.h> 22 #include <dirent.h> 23 #endif 24 #include <wchar.h> 25 #include <sys/stat.h> 26 #include "ft2_header.h" 27 #include "ft2_unicode.h" 28 #include "ft2_config.h" 29 #include "ft2_mouse.h" 30 #include "ft2_gui.h" 31 #include "ft2_pattern_ed.h" 32 #include "ft2_sample_loader.h" 33 #include "ft2_sample_saver.h" 34 #include "ft2_diskop.h" 35 #include "ft2_wav_renderer.h" 36 #include "ft2_module_loader.h" 37 #include "ft2_module_saver.h" 38 #include "ft2_events.h" 39 #include "ft2_video.h" 40 #include "ft2_inst_ed.h" 41 #include "ft2_structs.h" 42 43 // hide POSIX warnings for chdir() 44 #ifdef _MSC_VER 45 #pragma warning(disable: 4996) 46 #endif 47 48 #define FILENAME_TEXT_X 170 49 #define FILESIZE_TEXT_X 295 50 #define DISKOP_MAX_DRIVE_BUTTONS 8 51 52 #ifdef _WIN32 53 #define PARENT_DIR_STR L".." 54 static HANDLE hFind; 55 #else 56 #define PARENT_DIR_STR ".." 57 static DIR *hFind; 58 #endif 59 60 // "look for file" flags 61 enum 62 { 63 LFF_DONE = 0, 64 LFF_SKIP = 1, 65 LFF_OK = 2 66 }; 67 68 typedef struct DirRec 69 { 70 UNICHAR *nameU; 71 bool isDir; 72 int32_t filesize; 73 } DirRec; 74 75 static char FReq_SysReqText[256], *FReq_FileName, *FReq_NameTemp; 76 static char *modTmpFName, *insTmpFName, *smpTmpFName, *patTmpFName, *trkTmpFName; 77 static char *modTmpFNameUTF8; // for window title 78 static uint8_t FReq_Item; 79 static bool FReq_ShowAllFiles, insPathSet, smpPathSet, patPathSet, trkPathSet, firstTimeOpeningDiskOp = true; 80 static int32_t FReq_EntrySelected = -1, FReq_FileCount, FReq_DirPos, lastMouseY; 81 static UNICHAR *FReq_CurPathU, *FReq_ModCurPathU, *FReq_InsCurPathU, *FReq_SmpCurPathU, *FReq_PatCurPathU, *FReq_TrkCurPathU; 82 static DirRec *FReq_Buffer; 83 static SDL_Thread *thread; 84 85 static void setDiskOpItem(uint8_t item); 86 87 bool setupExecutablePath(void) 88 { 89 editor.binaryPathU = (UNICHAR *)malloc((PATH_MAX + 1) * sizeof (UNICHAR)); 90 if (editor.binaryPathU == NULL) 91 { 92 showErrorMsgBox("Not enough memory!"); 93 return false; 94 } 95 96 editor.binaryPathU[0] = 0; 97 UNICHAR_GETCWD(editor.binaryPathU, PATH_MAX); 98 99 return true; 100 } 101 102 int32_t getFileSize(UNICHAR *fileNameU) // returning -1 = filesize over 2GB 103 { 104 int64_t fSize; 105 106 #ifdef _WIN32 107 FILE *f = UNICHAR_FOPEN(fileNameU, "rb"); 108 if (f == NULL) 109 return 0; 110 111 _fseeki64(f, 0, SEEK_END); 112 fSize = _ftelli64(f); 113 fclose(f); 114 #else 115 struct stat st; 116 if (stat(fileNameU, &st) != 0) 117 return 0; 118 119 fSize = (int64_t)st.st_size; 120 #endif 121 if (fSize < 0) 122 fSize = 0; 123 124 if (fSize > INT32_MAX) 125 return -1; // -1 = ">2GB" flag 126 127 return (int32_t)fSize; 128 } 129 130 uint8_t getDiskOpItem(void) 131 { 132 return FReq_Item; 133 } 134 135 char *getCurrSongFilename(void) // for window title 136 { 137 return modTmpFNameUTF8; 138 } 139 140 void updateCurrSongFilename(void) // for window title 141 { 142 if (modTmpFNameUTF8 != NULL) 143 { 144 free(modTmpFNameUTF8); 145 modTmpFNameUTF8 = NULL; 146 } 147 148 if (modTmpFName == NULL) 149 return; 150 151 modTmpFNameUTF8 = cp850ToUtf8(modTmpFName); 152 } 153 154 // drive buttons for Windows 155 #ifdef _WIN32 156 static char logicalDriveNames[26][3] = 157 { 158 "A:", "B:", "C:", "D:", "E:", "F:", "G:", "H:", "I:", "J:", "K:", "L:", "M:", 159 "N:", "O:", "P:", "Q:", "R:", "S:", "T:", "U:", "V:", "W:", "X:", "Y:", "Z:" 160 }; 161 static uint32_t numLogicalDrives; 162 static uint32_t driveIndexes[DISKOP_MAX_DRIVE_BUTTONS]; 163 #endif 164 165 char *getDiskOpFilename(void) 166 { 167 return FReq_FileName; 168 } 169 170 const UNICHAR *getDiskOpCurPath(void) 171 { 172 return FReq_CurPathU; 173 } 174 175 const UNICHAR *getDiskOpModPath(void) 176 { 177 return FReq_ModCurPathU; 178 } 179 180 const UNICHAR *getDiskOpSmpPath(void) 181 { 182 return FReq_SmpCurPathU; 183 } 184 185 static void setupInitialPaths(void) 186 { 187 // the UNICHAR paths are already zeroed out 188 189 #ifdef _WIN32 190 if (config.modulesPath[0] != '\0') 191 { 192 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.modulesPath, -1, FReq_ModCurPathU, 80); 193 FReq_ModCurPathU[80] = 0; 194 } 195 196 if (config.instrPath[0] != '\0') 197 { 198 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.instrPath, -1, FReq_InsCurPathU, 80); 199 FReq_InsCurPathU[80] = 0; 200 insPathSet = true; 201 } 202 203 if (config.samplesPath[0] != '\0') 204 { 205 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.samplesPath, -1, FReq_SmpCurPathU, 80); 206 FReq_SmpCurPathU[80] = 0; 207 smpPathSet = true; 208 } 209 210 if (config.patternsPath[0] != '\0') 211 { 212 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.patternsPath, -1, FReq_PatCurPathU, 80); 213 FReq_PatCurPathU[80] = 0; 214 patPathSet = true; 215 } 216 217 if (config.tracksPath[0] != '\0') 218 { 219 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.tracksPath, -1, FReq_TrkCurPathU, 80); 220 FReq_TrkCurPathU[80] = 0; 221 trkPathSet = true; 222 } 223 #else 224 if (config.modulesPath[0] != '\0') 225 { 226 strncpy(FReq_ModCurPathU, config.modulesPath, 80); 227 FReq_ModCurPathU[80] = 0; 228 } 229 230 if (config.instrPath[0] != '\0') 231 { 232 strncpy(FReq_InsCurPathU, config.instrPath, 80); 233 FReq_InsCurPathU[80] = 0; 234 insPathSet = true; 235 } 236 237 if (config.samplesPath[0] != '\0') 238 { 239 strncpy(FReq_SmpCurPathU, config.samplesPath, 80); 240 FReq_SmpCurPathU[80] = 0; 241 smpPathSet = true; 242 } 243 244 if (config.patternsPath[0] != '\0') 245 { 246 strncpy(FReq_PatCurPathU, config.patternsPath, 80); 247 FReq_PatCurPathU[80] = 0; 248 patPathSet = true; 249 } 250 251 if (config.tracksPath[0] != '\0') 252 { 253 strncpy(FReq_TrkCurPathU, config.tracksPath, 80); 254 FReq_TrkCurPathU[80] = 0; 255 trkPathSet = true; 256 } 257 #endif 258 } 259 260 static void freeDirRecBuffer(void) 261 { 262 if (FReq_Buffer != NULL) 263 { 264 for (int32_t i = 0; i < FReq_FileCount; i++) 265 { 266 if (FReq_Buffer[i].nameU != NULL) 267 free(FReq_Buffer[i].nameU); 268 } 269 270 free(FReq_Buffer); 271 FReq_Buffer = NULL; 272 } 273 274 FReq_FileCount = 0; 275 } 276 277 void freeDiskOp(void) 278 { 279 if (editor.tmpFilenameU != NULL) 280 { 281 free(editor.tmpFilenameU); 282 editor.tmpFilenameU = NULL; 283 } 284 285 if (editor.tmpInstrFilenameU != NULL) 286 { 287 free(editor.tmpInstrFilenameU); 288 editor.tmpInstrFilenameU = NULL; 289 } 290 291 if (modTmpFName != NULL) { free(modTmpFName); modTmpFName = NULL; } 292 if (insTmpFName != NULL) { free(insTmpFName); insTmpFName = NULL; } 293 if (smpTmpFName != NULL) { free(smpTmpFName); smpTmpFName = NULL; } 294 if (patTmpFName != NULL) { free(patTmpFName); patTmpFName = NULL; } 295 if (trkTmpFName != NULL) { free(trkTmpFName); trkTmpFName = NULL; } 296 if (FReq_NameTemp != NULL) { free(FReq_NameTemp); FReq_NameTemp = NULL; } 297 if (FReq_ModCurPathU != NULL) { free(FReq_ModCurPathU); FReq_ModCurPathU = NULL; } 298 if (FReq_InsCurPathU != NULL) { free(FReq_InsCurPathU); FReq_InsCurPathU = NULL; } 299 if (FReq_SmpCurPathU != NULL) { free(FReq_SmpCurPathU); FReq_SmpCurPathU = NULL; } 300 if (FReq_PatCurPathU != NULL) { free(FReq_PatCurPathU); FReq_PatCurPathU = NULL; } 301 if (FReq_TrkCurPathU != NULL) { free(FReq_TrkCurPathU); FReq_TrkCurPathU = NULL; } 302 if (modTmpFNameUTF8 != NULL) { free(modTmpFNameUTF8); modTmpFNameUTF8 = NULL; } 303 304 freeDirRecBuffer(); 305 } 306 307 bool setupDiskOp(void) 308 { 309 modTmpFName = (char *)malloc((PATH_MAX + 1) * sizeof (char)); 310 insTmpFName = (char *)malloc((PATH_MAX + 1) * sizeof (char)); 311 smpTmpFName = (char *)malloc((PATH_MAX + 1) * sizeof (char)); 312 patTmpFName = (char *)malloc((PATH_MAX + 1) * sizeof (char)); 313 trkTmpFName = (char *)malloc((PATH_MAX + 1) * sizeof (char)); 314 FReq_NameTemp = (char *)malloc((PATH_MAX + 1) * sizeof (char)); 315 316 FReq_ModCurPathU = (UNICHAR *)malloc((PATH_MAX + 1) * sizeof (UNICHAR)); 317 FReq_InsCurPathU = (UNICHAR *)malloc((PATH_MAX + 1) * sizeof (UNICHAR)); 318 FReq_SmpCurPathU = (UNICHAR *)malloc((PATH_MAX + 1) * sizeof (UNICHAR)); 319 FReq_PatCurPathU = (UNICHAR *)malloc((PATH_MAX + 1) * sizeof (UNICHAR)); 320 FReq_TrkCurPathU = (UNICHAR *)malloc((PATH_MAX + 1) * sizeof (UNICHAR)); 321 322 if (modTmpFName == NULL || insTmpFName == NULL || smpTmpFName == NULL || 323 patTmpFName == NULL || trkTmpFName == NULL || FReq_NameTemp == NULL || 324 FReq_ModCurPathU == NULL || FReq_InsCurPathU == NULL || FReq_SmpCurPathU == NULL || 325 FReq_PatCurPathU == NULL || FReq_TrkCurPathU == NULL) 326 { 327 // allocated memory is free'd lateron 328 showErrorMsgBox("Not enough memory!"); 329 return false; 330 } 331 332 // clear first entry of strings 333 modTmpFName[0] = '\0'; 334 insTmpFName[0] = '\0'; 335 smpTmpFName[0] = '\0'; 336 patTmpFName[0] = '\0'; 337 trkTmpFName[0] = '\0'; 338 FReq_NameTemp[0] = '\0'; 339 FReq_ModCurPathU[0] = 0; 340 FReq_InsCurPathU[0] = 0; 341 FReq_SmpCurPathU[0] = 0; 342 FReq_PatCurPathU[0] = 0; 343 FReq_TrkCurPathU[0] = 0; 344 345 strcpy(modTmpFName, "untitled.xm"); 346 strcpy(insTmpFName, "untitled.xi"); 347 strcpy(smpTmpFName, "untitled.wav"); 348 strcpy(patTmpFName, "untitled.xp"); 349 strcpy(trkTmpFName, "untitled.xt"); 350 351 setupInitialPaths(); 352 setDiskOpItem(0); 353 354 updateCurrSongFilename(); // for window title 355 updateWindowTitle(true); 356 357 return true; 358 } 359 360 int32_t getExtOffset(char *s, int32_t stringLen) // get byte offset of file extension (last '.') 361 { 362 if (s == NULL || stringLen < 1) 363 return -1; 364 365 for (int32_t i = stringLen - 1; i >= 0; i--) 366 { 367 if (i != 0 && s[i] == '.') 368 return i; 369 } 370 371 return -1; 372 } 373 374 static void removeQuestionmarksFromString(char *s) 375 { 376 if (s == NULL || *s == '\0') 377 return; 378 379 const int32_t len = (int32_t)strlen(s); 380 for (int32_t i = 0; i < len; i++) 381 { 382 if (s[i] == '?') 383 s[i] = ' ' ; 384 } 385 } 386 387 #ifdef _WIN32 // WINDOWS SPECIFIC FILE OPERATION ROUTINES 388 389 bool fileExistsAnsi(char *str) 390 { 391 UNICHAR *strU = cp850ToUnichar(str); 392 if (strU == NULL) 393 return false; 394 395 bool retVal = PathFileExistsW(strU); 396 free(strU); 397 398 return retVal; 399 } 400 401 static bool deleteDirRecursive(UNICHAR *strU) 402 { 403 SHFILEOPSTRUCTW shfo; 404 405 memset(&shfo, 0, sizeof (shfo)); 406 shfo.wFunc = FO_DELETE; 407 shfo.fFlags = FOF_SILENT | FOF_NOERRORUI | FOF_NOCONFIRMATION; 408 shfo.pFrom = strU; 409 410 return (SHFileOperationW(&shfo) == 0); 411 } 412 413 static bool makeDirAnsi(char *str) 414 { 415 UNICHAR *strU = cp850ToUnichar(str); 416 if (strU == NULL) 417 return false; 418 419 int32_t retVal = _wmkdir(strU); 420 free(strU); 421 422 return (retVal == 0); 423 } 424 425 static bool renameAnsi(UNICHAR *oldNameU, char *newName) 426 { 427 UNICHAR *newNameU = cp850ToUnichar(newName); 428 if (newNameU == NULL) 429 return false; 430 431 int32_t retVal = UNICHAR_RENAME(oldNameU, newNameU); 432 free(newNameU); 433 434 return (retVal == 0); 435 } 436 437 static void setupDiskOpDrives(void) // Windows only 438 { 439 fillRect(134, 29, 31, 111, PAL_DESKTOP); 440 numLogicalDrives = 0; 441 442 // get number of drives and drive names 443 const uint32_t drivesBitmask = GetLogicalDrives(); 444 for (int32_t i = 0; i < 8*sizeof (uint32_t); i++) 445 { 446 if ((drivesBitmask & (1 << i)) != 0) 447 { 448 driveIndexes[numLogicalDrives++] = i; 449 if (numLogicalDrives == DISKOP_MAX_DRIVE_BUTTONS) 450 break; 451 } 452 } 453 454 // hide all buttons 455 for (uint16_t i = 0; i < DISKOP_MAX_DRIVE_BUTTONS; i++) 456 hidePushButton(PB_DISKOP_DRIVE1 + i); 457 458 // set button names and show buttons 459 for (uint16_t i = 0; i < numLogicalDrives; i++) 460 { 461 pushButtons[PB_DISKOP_DRIVE1 + i].caption = logicalDriveNames[driveIndexes[i]]; 462 showPushButton(PB_DISKOP_DRIVE1 + i); 463 } 464 } 465 466 static void openDrive(char *str) // Windows only 467 { 468 if (mouse.mode == MOUSE_MODE_DELETE) 469 { 470 okBox(0, "System message", "Drive deletion is not implemented!", NULL); 471 return; 472 } 473 474 if (str == NULL || *str == '\0') 475 { 476 okBox(0, "System message", "Couldn't open drive!", NULL); 477 return; 478 } 479 480 if (chdir(str) != 0) 481 okBox(0, "System message", "Couldn't open drive! Please make sure there's a disk in it.", NULL); 482 else 483 editor.diskOpReadDir = true; 484 } 485 486 #else // NON-WINDOWS SPECIFIC FILE OPERATION ROUTINES 487 488 bool fileExistsAnsi(char *str) 489 { 490 UNICHAR *strU = cp850ToUnichar(str); 491 if (strU == NULL) 492 return false; 493 494 int32_t retVal = access(strU, F_OK); 495 free(strU); 496 497 return (retVal != -1); 498 } 499 500 static bool deleteDirRecursive(UNICHAR *strU) 501 { 502 FTSENT *curr; 503 char *files[] = { (char *)(strU), NULL }; 504 505 FTS *ftsp = fts_open(files, FTS_NOCHDIR | FTS_PHYSICAL | FTS_XDEV, NULL); 506 if (!ftsp) 507 return false; 508 509 bool ret = true; 510 while ((curr = fts_read(ftsp))) 511 { 512 switch (curr->fts_info) 513 { 514 default: 515 case FTS_NS: 516 case FTS_DNR: 517 case FTS_ERR: 518 ret = false; 519 break; 520 521 case FTS_D: 522 case FTS_DC: 523 case FTS_DOT: 524 case FTS_NSOK: 525 break; 526 527 case FTS_DP: 528 case FTS_F: 529 case FTS_SL: 530 case FTS_SLNONE: 531 case FTS_DEFAULT: 532 { 533 if (remove(curr->fts_accpath) < 0) 534 ret = false; 535 } 536 break; 537 } 538 } 539 540 if (ftsp != NULL) 541 fts_close(ftsp); 542 543 return ret; 544 } 545 546 static bool makeDirAnsi(char *str) 547 { 548 UNICHAR *strU = cp850ToUnichar(str); 549 if (strU == NULL) 550 return false; 551 552 int32_t retVal = mkdir(str, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 553 free(strU); 554 555 return (retVal == 0); 556 } 557 558 static bool renameAnsi(UNICHAR *oldNameU, char *newName) 559 { 560 int32_t retVal; 561 UNICHAR *newNameU; 562 563 newNameU = cp850ToUnichar(newName); 564 if (newNameU == NULL) 565 return false; 566 567 retVal = UNICHAR_RENAME(oldNameU, newNameU); 568 free(newNameU); 569 570 return (retVal == 0); 571 } 572 #endif 573 574 static void openDirectory(UNICHAR *strU) 575 { 576 if (strU == NULL || UNICHAR_STRLEN(strU) == 0) 577 { 578 okBox(0, "System message", "Couldn't open directory! No permission or in use?", NULL); 579 return; 580 } 581 582 if (UNICHAR_CHDIR(strU) != 0) 583 okBox(0, "System message", "Couldn't open directory! No permission or in use?", NULL); 584 else 585 editor.diskOpReadDir = true; 586 } 587 588 bool diskOpGoParent(void) 589 { 590 if (chdir("..") == 0) 591 { 592 editor.diskOpReadDir = true; 593 FReq_EntrySelected = -1; 594 595 return true; 596 } 597 598 return false; 599 } 600 601 static char *getFilenameFromPath(char *p) 602 { 603 int32_t i; 604 605 if (p == NULL || p[0] == '\0') 606 return p; 607 608 const int32_t len = (int32_t)strlen(p); 609 if (len < 2 || p[len-1] == DIR_DELIMITER) 610 return p; 611 612 // search for last directory delimiter 613 for (i = len - 1; i >= 0; i--) 614 { 615 if (p[i] == DIR_DELIMITER) 616 break; 617 } 618 619 if (i != 0) 620 p += i+1; // we found a directory delimiter - skip to the last one 621 622 return p; 623 } 624 625 void sanitizeFilename(const char *src) 626 { 627 // some of these are legal on GNU/Linux and macOS, but whatever... 628 const char illegalChars[] = "\\/:*?\"<>|"; 629 char *ptr; 630 631 if (src == NULL || src[0] == '\0') 632 return; 633 634 // convert illegal characters to space (for making a filename the OS will accept) 635 while ((ptr = (char *)strpbrk(src, illegalChars)) != NULL) 636 *ptr = ' '; 637 } 638 639 void diskOpSetFilename(uint8_t type, UNICHAR *pathU) 640 { 641 char *ansiPath = unicharToCp850(pathU, true); 642 if (ansiPath == NULL) 643 return; 644 645 char *filename = getFilenameFromPath(ansiPath); 646 uint32_t filenameLen = (uint32_t)strlen(filename); 647 648 if (filenameLen > PATH_MAX) 649 { 650 free(ansiPath); 651 return; // filename is too long, don't bother to copy it over 652 } 653 654 sanitizeFilename(filename); 655 656 switch (type) 657 { 658 default: 659 case DISKOP_ITEM_MODULE: 660 { 661 strcpy(modTmpFName, filename); 662 updateCurrSongFilename(); // for window title 663 664 if (editor.moduleSaveMode == MOD_SAVE_MODE_MOD) 665 changeFilenameExt(modTmpFName, ".mod", PATH_MAX); 666 else if (editor.moduleSaveMode == MOD_SAVE_MODE_XM) 667 changeFilenameExt(modTmpFName, ".xm", PATH_MAX); 668 else if (editor.moduleSaveMode == MOD_SAVE_MODE_WAV) 669 changeFilenameExt(modTmpFName, ".wav", PATH_MAX); 670 671 updateWindowTitle(true); 672 } 673 break; 674 675 case DISKOP_ITEM_INSTR: 676 strcpy(insTmpFName, filename); 677 break; 678 679 case DISKOP_ITEM_SAMPLE: 680 { 681 strcpy(smpTmpFName, filename); 682 683 if (editor.sampleSaveMode == SMP_SAVE_MODE_RAW) 684 changeFilenameExt(smpTmpFName, ".raw", PATH_MAX); 685 else if (editor.sampleSaveMode == SMP_SAVE_MODE_IFF) 686 changeFilenameExt(smpTmpFName, ".iff", PATH_MAX); 687 else if (editor.sampleSaveMode == SMP_SAVE_MODE_WAV) 688 changeFilenameExt(smpTmpFName, ".wav", PATH_MAX); 689 } 690 break; 691 692 case DISKOP_ITEM_PATTERN: 693 strcpy(patTmpFName, filename); 694 break; 695 696 case DISKOP_ITEM_TRACK: 697 strcpy(trkTmpFName, filename); 698 break; 699 } 700 701 free(ansiPath); 702 703 if (ui.diskOpShown) 704 drawTextBox(TB_DISKOP_FILENAME); 705 } 706 707 static void openFile(UNICHAR *filenameU, bool songModifiedCheck) 708 { 709 // first check if we can actually open the requested file 710 FILE *f = UNICHAR_FOPEN(filenameU, "rb"); 711 if (f == NULL) 712 { 713 okBox(0, "System message", "Couldn't open file/directory! No permission or in use?", NULL); 714 return; 715 } 716 fclose(f); 717 718 const int32_t filesize = getFileSize(filenameU); 719 if (filesize == -1) // >2GB 720 { 721 okBox(0, "System message", "The file is too big and can't be loaded (over 2GB).", NULL); 722 return; 723 } 724 725 if (filesize >= 128L*1024*1024) // 128MB 726 { 727 if (okBox(2, "System request", "Are you sure you want to load such a big file?", NULL) != 1) 728 return; 729 } 730 731 // file is readable, handle file... 732 switch (FReq_Item) 733 { 734 default: 735 case DISKOP_ITEM_MODULE: 736 { 737 if (songModifiedCheck && song.isModified) 738 { 739 // remove file selection before okBox() opens up 740 FReq_EntrySelected = -1; 741 diskOp_DrawFilelist(); 742 743 if (okBox(2, "System request", "You have unsaved changes in your song. Load new song and lose all changes?", NULL) != 1) 744 return; 745 } 746 747 editor.loadMusicEvent = EVENT_LOADMUSIC_DISKOP; 748 loadMusic(filenameU); 749 } 750 break; 751 752 case DISKOP_ITEM_INSTR: 753 loadInstr(filenameU); 754 break; 755 756 case DISKOP_ITEM_SAMPLE: 757 loadSample(filenameU, editor.curSmp, false); 758 break; 759 760 case DISKOP_ITEM_PATTERN: 761 loadPattern(filenameU); 762 break; 763 764 case DISKOP_ITEM_TRACK: 765 loadTrack(filenameU); 766 break; 767 } 768 } 769 770 static void removeFilenameExt(char *name) 771 { 772 if (name == NULL || *name == '\0') 773 return; 774 775 const int32_t len = (int32_t)strlen(name); 776 777 const int32_t extOffset = getExtOffset(name, len); 778 if (extOffset != -1) 779 name[extOffset] = '\0'; 780 } 781 782 void changeFilenameExt(char *name, char *ext, int32_t nameMaxLen) 783 { 784 if (name == NULL || name[0] == '\0' || ext == NULL) 785 return; 786 787 removeFilenameExt(name); 788 789 const int32_t len = (int32_t)strlen(name); 790 int32_t extLen = (int32_t)strlen(ext); 791 792 if (len+extLen > nameMaxLen) 793 extLen = nameMaxLen-len; 794 795 strncat(name, ext, extLen); 796 797 if (ui.diskOpShown) 798 diskOp_DrawDirectory(); 799 } 800 801 void diskOpChangeFilenameExt(char *ext) 802 { 803 changeFilenameExt(FReq_FileName, ext, PATH_MAX); 804 } 805 806 void trimEntryName(char *name, bool isDir) 807 { 808 char extBuffer[24]; 809 810 int32_t j = (int32_t)strlen(name); 811 const int32_t extOffset = getExtOffset(name, j); 812 int32_t extLen = (int32_t)strlen(&name[extOffset]); 813 j--; 814 815 if (isDir) 816 { 817 // directory 818 while (textWidth(name) > 160-8 && j >= 2) 819 { 820 name[j-2] = '.'; 821 name[j-1] = '.'; 822 name[j-0] = '\0'; 823 j--; 824 } 825 826 return; 827 } 828 829 if (extOffset != -1 && extLen <= 4) 830 { 831 // has extension 832 sprintf(extBuffer, ".. %s", &name[extOffset]); // "testtestte... .xm" 833 834 extLen = (int32_t)strlen(extBuffer); 835 while (textWidth(name) >= FILESIZE_TEXT_X-FILENAME_TEXT_X && j >= extLen+1) 836 { 837 memcpy(&name[j - extLen], extBuffer, extLen + 1); 838 j--; 839 } 840 } 841 else 842 { 843 // no extension 844 while (textWidth(name) >= FILESIZE_TEXT_X-FILENAME_TEXT_X && j >= 2) 845 { 846 name[j-2] = '.'; 847 name[j-1] = '.'; 848 name[j-0] = '\0'; 849 j--; 850 } 851 } 852 } 853 854 void createFileOverwriteText(char *filename, char *buffer) 855 { 856 char nameTmp[128]; 857 858 // read entry name to a small buffer 859 const uint32_t nameLen = (uint32_t)strlen(filename); 860 memcpy(nameTmp, filename, (nameLen >= sizeof (nameTmp)) ? sizeof (nameTmp) : (nameLen + 1)); 861 nameTmp[sizeof (nameTmp) - 1] = '\0'; 862 863 trimEntryName(nameTmp, false); 864 865 sprintf(buffer, "Overwrite file \"%s\"?", nameTmp); 866 } 867 868 static void diskOpSave(bool checkOverwrite) 869 { 870 UNICHAR *fileNameU; 871 872 if (FReq_FileName[0] == '\0') 873 { 874 okBox(0, "System message", "Filename can't be empty!", NULL); 875 return; 876 } 877 878 // test if the very first character has a dot... 879 if (FReq_FileName[0] == '.') 880 { 881 okBox(0, "System message", "The very first character in the filename can't be '.' (dot)!", NULL); 882 return; 883 } 884 885 // test for illegal file name 886 if (FReq_FileName[0] == '\0' || strpbrk(FReq_FileName, "\\/:*?\"<>|") != NULL) 887 { 888 okBox(0, "System message", "The filename can't contain the following characters: \\ / : * ? \" < > |", NULL); 889 return; 890 } 891 892 switch (FReq_Item) 893 { 894 default: 895 case DISKOP_ITEM_MODULE: 896 { 897 switch (editor.moduleSaveMode) 898 { 899 case MOD_SAVE_MODE_MOD: diskOpChangeFilenameExt(".mod"); break; 900 default: case MOD_SAVE_MODE_XM: diskOpChangeFilenameExt(".xm"); break; 901 case MOD_SAVE_MODE_WAV: diskOpChangeFilenameExt(".wav"); break; 902 } 903 904 // enter WAV renderer if needed 905 if (editor.moduleSaveMode == MOD_SAVE_MODE_WAV) 906 { 907 exitDiskOpScreen(); 908 showWavRenderer(); 909 return; 910 } 911 912 if (checkOverwrite && fileExistsAnsi(FReq_FileName)) 913 { 914 createFileOverwriteText(FReq_FileName, FReq_SysReqText); 915 if (okBox(2, "System request", FReq_SysReqText, NULL) != 1) 916 return; 917 } 918 919 fileNameU = cp850ToUnichar(FReq_FileName); 920 if (fileNameU == NULL) 921 { 922 okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL); 923 return; 924 } 925 926 saveMusic(fileNameU); 927 free(fileNameU); 928 // sets editor.diskOpReadDir after thread is done 929 } 930 break; 931 932 case DISKOP_ITEM_INSTR: 933 { 934 diskOpChangeFilenameExt(".xi"); 935 936 if (checkOverwrite && fileExistsAnsi(FReq_FileName)) 937 { 938 createFileOverwriteText(FReq_FileName, FReq_SysReqText); 939 if (okBox(2, "System request", FReq_SysReqText, NULL) != 1) 940 return; 941 } 942 943 fileNameU = cp850ToUnichar(FReq_FileName); 944 if (fileNameU == NULL) 945 { 946 okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL); 947 return; 948 } 949 950 saveInstr(fileNameU, editor.curInstr); 951 free(fileNameU); 952 // editor.diskOpReadDir is set after thread is done 953 } 954 break; 955 956 case DISKOP_ITEM_SAMPLE: 957 { 958 switch (editor.sampleSaveMode) 959 { 960 case SMP_SAVE_MODE_RAW: diskOpChangeFilenameExt(".raw"); break; 961 case SMP_SAVE_MODE_IFF: diskOpChangeFilenameExt(".iff"); break; 962 default: case SMP_SAVE_MODE_WAV: diskOpChangeFilenameExt(".wav"); break; 963 } 964 965 if (checkOverwrite && fileExistsAnsi(FReq_FileName)) 966 { 967 createFileOverwriteText(FReq_FileName, FReq_SysReqText); 968 if (okBox(2, "System request", FReq_SysReqText, NULL) != 1) 969 return; 970 } 971 972 fileNameU = cp850ToUnichar(FReq_FileName); 973 if (fileNameU == NULL) 974 { 975 okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL); 976 return; 977 } 978 979 saveSample(fileNameU, SAVE_NORMAL); 980 free(fileNameU); 981 // editor.diskOpReadDir is set after thread is done 982 } 983 break; 984 985 case DISKOP_ITEM_PATTERN: 986 { 987 diskOpChangeFilenameExt(".xp"); 988 989 if (checkOverwrite && fileExistsAnsi(FReq_FileName)) 990 { 991 createFileOverwriteText(FReq_FileName, FReq_SysReqText); 992 if (okBox(2, "System request", FReq_SysReqText, NULL) != 1) 993 return; 994 } 995 996 fileNameU = cp850ToUnichar(FReq_FileName); 997 if (fileNameU == NULL) 998 { 999 okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL); 1000 return; 1001 } 1002 1003 editor.diskOpReadDir = savePattern(fileNameU); 1004 free(fileNameU); 1005 } 1006 break; 1007 1008 case DISKOP_ITEM_TRACK: 1009 { 1010 diskOpChangeFilenameExt(".xt"); 1011 1012 if (checkOverwrite && fileExistsAnsi(FReq_FileName)) 1013 { 1014 createFileOverwriteText(FReq_FileName, FReq_SysReqText); 1015 if (okBox(2, "System request", FReq_SysReqText, NULL) != 1) 1016 return; 1017 } 1018 1019 fileNameU = cp850ToUnichar(FReq_FileName); 1020 if (fileNameU == NULL) 1021 { 1022 okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL); 1023 return; 1024 } 1025 1026 editor.diskOpReadDir = saveTrack(fileNameU); 1027 free(fileNameU); 1028 } 1029 break; 1030 } 1031 } 1032 1033 void pbDiskOpSave(void) 1034 { 1035 diskOpSave(config.cfg_OverwriteWarning ? true : false); // check if about to overwrite 1036 } 1037 1038 static void fileListPressed(int32_t index) 1039 { 1040 char *nameTmp; 1041 int32_t result; 1042 1043 const int32_t entryIndex = FReq_DirPos + index; 1044 if (entryIndex >= FReq_FileCount || FReq_FileCount == 0) 1045 return; // illegal entry 1046 1047 const int8_t mode = mouse.mode; 1048 1049 // set normal mouse cursor 1050 if (mouse.mode != MOUSE_MODE_NORMAL) 1051 setMouseMode(MOUSE_MODE_NORMAL); 1052 1053 // remove file selection 1054 FReq_EntrySelected = -1; 1055 diskOp_DrawFilelist(); 1056 1057 DirRec *dirEntry = &FReq_Buffer[entryIndex]; 1058 switch (mode) 1059 { 1060 // open file/folder 1061 default: 1062 case MOUSE_MODE_NORMAL: 1063 { 1064 if (dirEntry->isDir) 1065 openDirectory(dirEntry->nameU); 1066 else 1067 openFile(dirEntry->nameU, true); 1068 } 1069 break; 1070 1071 // delete file/folder 1072 case MOUSE_MODE_DELETE: 1073 { 1074 if (!dirEntry->isDir || UNICHAR_STRCMP(dirEntry->nameU, PARENT_DIR_STR)) // don't handle ".." dir 1075 { 1076 nameTmp = unicharToCp850(dirEntry->nameU, true); 1077 if (nameTmp == NULL) 1078 break; 1079 1080 trimEntryName(nameTmp, dirEntry->isDir); 1081 1082 if (dirEntry->isDir) 1083 sprintf(FReq_SysReqText, "Delete directory \"%s\"?", nameTmp); 1084 else 1085 sprintf(FReq_SysReqText, "Delete file \"%s\"?", nameTmp); 1086 1087 free(nameTmp); 1088 1089 if (okBox(2, "System request", FReq_SysReqText, NULL) == 1) 1090 { 1091 if (dirEntry->isDir) 1092 { 1093 result = deleteDirRecursive(dirEntry->nameU); 1094 if (!result) 1095 okBox(0, "System message", "Couldn't delete folder: Access denied!", NULL); 1096 else 1097 editor.diskOpReadDir = true; 1098 } 1099 else 1100 { 1101 result = (UNICHAR_REMOVE(dirEntry->nameU) == 0); 1102 if (!result) 1103 okBox(0, "System message", "Couldn't delete file: Access denied!", NULL); 1104 else 1105 editor.diskOpReadDir = true; 1106 } 1107 } 1108 } 1109 } 1110 break; 1111 1112 // rename file/folder 1113 case MOUSE_MODE_RENAME: 1114 { 1115 if (dirEntry->isDir || UNICHAR_STRCMP(dirEntry->nameU, PARENT_DIR_STR)) // don't handle ".." dir 1116 { 1117 nameTmp = unicharToCp850(dirEntry->nameU, true); 1118 if (nameTmp == NULL) 1119 break; 1120 1121 strncpy(FReq_NameTemp, nameTmp, PATH_MAX); 1122 FReq_NameTemp[PATH_MAX] = '\0'; 1123 free(nameTmp); 1124 1125 // in case of UTF8 -> CP437 encoding failure, there can be question marks. Remove them... 1126 removeQuestionmarksFromString(FReq_NameTemp); 1127 1128 if (inputBox(1, dirEntry->isDir ? "Enter new directory name:" : "Enter new filename:", FReq_NameTemp, PATH_MAX) == 1) 1129 { 1130 if (FReq_NameTemp == NULL || FReq_NameTemp[0] == '\0') 1131 { 1132 okBox(0, "System message", "New name can't be empty!", NULL); 1133 break; 1134 } 1135 1136 if (!renameAnsi(dirEntry->nameU, FReq_NameTemp)) 1137 { 1138 if (dirEntry->isDir) 1139 okBox(0, "System message", "Couldn't rename directory: Access denied, or dir already exists!", NULL); 1140 else 1141 okBox(0, "System message", "Couldn't rename file: Access denied, or file already exists!", NULL); 1142 } 1143 else 1144 { 1145 editor.diskOpReadDir = true; 1146 } 1147 } 1148 } 1149 } 1150 break; 1151 } 1152 } 1153 1154 bool testDiskOpMouseDown(bool mouseHeldDlown) 1155 { 1156 int32_t tmpEntry; 1157 1158 if (!ui.diskOpShown || FReq_FileCount == 0) 1159 return false; 1160 1161 int32_t max = FReq_FileCount - FReq_DirPos; 1162 if (max > DISKOP_ENTRY_NUM) // needed kludge when mouse-scrolling 1163 max = DISKOP_ENTRY_NUM; 1164 1165 if (!mouseHeldDlown) // select file 1166 { 1167 FReq_EntrySelected = -1; 1168 1169 if (mouse.x >= 169 && mouse.x <= 331 && mouse.y >= 4 && mouse.y <= 168) 1170 { 1171 tmpEntry = (mouse.y - 4) / (FONT1_CHAR_H + 1); 1172 if (tmpEntry >= 0 && tmpEntry < max) 1173 { 1174 FReq_EntrySelected = tmpEntry; 1175 diskOp_DrawFilelist(); 1176 } 1177 1178 mouse.lastUsedObjectType = OBJECT_DISKOPLIST; 1179 return true; 1180 } 1181 1182 return false; 1183 } 1184 1185 // handle scrolling if outside of disk op. list area 1186 if (mouse.y < 4) 1187 { 1188 scrollBarScrollUp(SB_DISKOP_LIST, 1); 1189 FReq_EntrySelected = -1; 1190 } 1191 else if (mouse.y > 168) 1192 { 1193 scrollBarScrollDown(SB_DISKOP_LIST, 1); 1194 FReq_EntrySelected = -1; 1195 } 1196 1197 if (mouse.y == lastMouseY) 1198 return true; 1199 1200 lastMouseY = mouse.y; 1201 1202 tmpEntry = (mouse.y - 4) / (FONT1_CHAR_H + 1); 1203 if (mouse.x < 169 || mouse.x > 331 || mouse.y < 4 || tmpEntry < 0 || tmpEntry >= max) 1204 { 1205 FReq_EntrySelected = -1; 1206 diskOp_DrawFilelist(); 1207 1208 return true; 1209 } 1210 1211 if (tmpEntry != FReq_EntrySelected) 1212 { 1213 FReq_EntrySelected = tmpEntry; 1214 diskOp_DrawFilelist(); 1215 } 1216 1217 return true; 1218 } 1219 1220 void testDiskOpMouseRelease(void) 1221 { 1222 if (ui.diskOpShown && FReq_EntrySelected != -1) 1223 { 1224 if (mouse.x >= 169 && mouse.x <= 329 && mouse.y >= 4 && mouse.y <= 168) 1225 fileListPressed((mouse.y - 4) / (FONT1_CHAR_H + 1)); 1226 1227 FReq_EntrySelected = -1; 1228 diskOp_DrawFilelist(); 1229 } 1230 } 1231 1232 static bool moduleExtensionAccepted(char *extPtr) 1233 { 1234 int32_t i = 0; 1235 while (true) 1236 { 1237 const char *str = supportedModExtensions[i++]; 1238 if (!_stricmp(str, "END_OF_LIST")) 1239 return false; 1240 1241 if (!_stricmp(str, extPtr)) 1242 break; 1243 } 1244 1245 return true; 1246 } 1247 1248 static bool sampleExtensionAccepted(char *extPtr) 1249 { 1250 int32_t i = 0; 1251 while (true) 1252 { 1253 const char *str = supportedSmpExtensions[i++]; 1254 if (!_stricmp(str, "END_OF_LIST")) 1255 return false; 1256 1257 if (!_stricmp(str, extPtr)) 1258 break; 1259 } 1260 1261 return true; 1262 } 1263 1264 static uint8_t handleEntrySkip(UNICHAR *nameU, bool isDir) 1265 { 1266 // skip if illegal name or filesize >32-bit 1267 if (nameU == NULL) 1268 return true; 1269 1270 char *name = unicharToCp850(nameU, false); 1271 if (name == NULL) 1272 return true; 1273 1274 if (name[0] == '\0') 1275 goto skipEntry; 1276 1277 const int32_t nameLen = (int32_t)strlen(name); 1278 1279 // skip ".name" dirs/files 1280 if (nameLen >= 2 && name[0] == '.' && name[1] != '.') 1281 goto skipEntry; 1282 1283 if (isDir) 1284 { 1285 // skip '.' directory 1286 if (nameLen == 1 && name[0] == '.') 1287 goto skipEntry; 1288 1289 // macOS/Linux: skip '..' directory if we're in root 1290 #ifndef _WIN32 1291 if (nameLen == 2 && name[0] == '.' && name[1] == '.') 1292 { 1293 if (FReq_CurPathU[0] == '/' && FReq_CurPathU[1] == '\0') 1294 goto skipEntry; 1295 } 1296 #endif 1297 } 1298 else if (!FReq_ShowAllFiles) 1299 { 1300 int32_t extOffset = getExtOffset(name, nameLen); 1301 if (extOffset == -1) 1302 goto skipEntry; 1303 extOffset++; // skip '.' 1304 1305 const int32_t extLen = (int32_t)strlen(&name[extOffset]); 1306 if (extLen < 2 || extLen > 6) 1307 goto skipEntry; // no possibly known extensions to filter out 1308 1309 char *extPtr = &name[extOffset]; 1310 1311 // decide what entries to keep based on file extension 1312 switch (FReq_Item) 1313 { 1314 default: 1315 case DISKOP_ITEM_MODULE: 1316 { 1317 if (editor.moduleSaveMode == MOD_SAVE_MODE_WAV && !_stricmp("wav", extPtr)) 1318 break; // show .wav files when save mode is "WAV" 1319 1320 if (!moduleExtensionAccepted(extPtr)) 1321 goto skipEntry; 1322 } 1323 break; 1324 1325 case DISKOP_ITEM_INSTR: 1326 { 1327 if (!_stricmp("xi", extPtr)) 1328 break; 1329 1330 if (!sampleExtensionAccepted(extPtr)) 1331 goto skipEntry; 1332 } 1333 break; 1334 1335 case DISKOP_ITEM_SAMPLE: 1336 { 1337 if (!sampleExtensionAccepted(extPtr)) 1338 goto skipEntry; 1339 } 1340 break; 1341 1342 case DISKOP_ITEM_PATTERN: 1343 { 1344 if (!_stricmp("xp", extPtr)) 1345 break; 1346 1347 goto skipEntry; 1348 } 1349 break; 1350 1351 case DISKOP_ITEM_TRACK: 1352 { 1353 if (!_stricmp("xt", extPtr)) 1354 break; 1355 1356 goto skipEntry; 1357 } 1358 break; 1359 } 1360 } 1361 1362 free(name); 1363 return false; // "Show All Files" mode is enabled, don't skip entry 1364 1365 skipEntry: 1366 free(name); 1367 return true; 1368 } 1369 1370 static int8_t findFirst(DirRec *searchRec) 1371 { 1372 #ifdef _WIN32 1373 WIN32_FIND_DATAW fData; 1374 #else 1375 struct dirent *fData; 1376 struct stat st; 1377 int64_t fSize; 1378 #endif 1379 1380 #if defined(__sun) || defined(sun) 1381 struct stat s; 1382 #endif 1383 1384 searchRec->nameU = NULL; // this one must be initialized 1385 1386 #ifdef _WIN32 1387 hFind = FindFirstFileW(L"*", &fData); 1388 if (hFind == NULL || hFind == INVALID_HANDLE_VALUE) 1389 return LFF_DONE; 1390 1391 searchRec->nameU = UNICHAR_STRDUP(fData.cFileName); 1392 if (searchRec->nameU == NULL) 1393 return LFF_SKIP; 1394 1395 searchRec->filesize = (fData.nFileSizeHigh > 0) ? -1 : fData.nFileSizeLow; 1396 searchRec->isDir = (fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false; 1397 #else 1398 hFind = opendir("."); 1399 if (hFind == NULL) 1400 return LFF_DONE; 1401 1402 fData = readdir(hFind); 1403 if (fData == NULL) 1404 return LFF_DONE; 1405 1406 searchRec->nameU = UNICHAR_STRDUP(fData->d_name); 1407 if (searchRec->nameU == NULL) 1408 return LFF_SKIP; 1409 1410 searchRec->filesize = 0; 1411 1412 #if defined(__sun) || defined(sun) 1413 stat(fData->d_name, &s); 1414 searchRec->isDir = (s.st_mode != S_IFDIR) ? true : false; 1415 #else 1416 searchRec->isDir = (fData->d_type == DT_DIR) ? true : false; 1417 #endif 1418 1419 #if defined(__sun) || defined(sun) 1420 if (s.st_mode == S_IFLNK) 1421 #else 1422 if (fData->d_type == DT_UNKNOWN || fData->d_type == DT_LNK) 1423 #endif 1424 { 1425 if (stat(fData->d_name, &st) == 0) 1426 { 1427 fSize = (int64_t)st.st_size; 1428 searchRec->filesize = (fSize > INT32_MAX) ? -1 : (fSize & 0xFFFFFFFF); 1429 1430 if ((st.st_mode & S_IFMT) == S_IFDIR) 1431 searchRec->isDir = true; 1432 } 1433 } 1434 else if (!searchRec->isDir) 1435 { 1436 if (stat(fData->d_name, &st) == 0) 1437 { 1438 fSize = (int64_t)st.st_size; 1439 searchRec->filesize = (fSize > INT32_MAX) ? -1 : (fSize & 0xFFFFFFFF); 1440 } 1441 } 1442 #endif 1443 1444 if (searchRec->filesize < -1) 1445 searchRec->filesize = -1; 1446 1447 if (handleEntrySkip(searchRec->nameU, searchRec->isDir)) 1448 { 1449 // skip file 1450 free(searchRec->nameU); 1451 searchRec->nameU = NULL; 1452 1453 return LFF_SKIP; 1454 } 1455 1456 return LFF_OK; 1457 } 1458 1459 static int8_t findNext(DirRec *searchRec) 1460 { 1461 #ifdef _WIN32 1462 WIN32_FIND_DATAW fData; 1463 #else 1464 struct dirent *fData; 1465 struct stat st; 1466 int64_t fSize; 1467 #endif 1468 1469 #if defined(__sun) || defined(sun) 1470 struct stat s; 1471 #endif 1472 1473 searchRec->nameU = NULL; // important 1474 1475 #ifdef _WIN32 1476 if (hFind == NULL || FindNextFileW(hFind, &fData) == 0) 1477 return LFF_DONE; 1478 1479 searchRec->nameU = UNICHAR_STRDUP(fData.cFileName); 1480 if (searchRec->nameU == NULL) 1481 return LFF_SKIP; 1482 1483 searchRec->filesize = (fData.nFileSizeHigh > 0) ? -1 : fData.nFileSizeLow; 1484 searchRec->isDir = (fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false; 1485 #else 1486 if (hFind == NULL || (fData = readdir(hFind)) == NULL) 1487 return LFF_DONE; 1488 1489 searchRec->nameU = UNICHAR_STRDUP(fData->d_name); 1490 if (searchRec->nameU == NULL) 1491 return LFF_SKIP; 1492 1493 searchRec->filesize = 0; 1494 1495 #if defined(__sun) || defined(sun) 1496 stat(fData->d_name, &s); 1497 searchRec->isDir = (s.st_mode != S_IFDIR) ? true : false; 1498 #else 1499 searchRec->isDir = (fData->d_type == DT_DIR) ? true : false; 1500 #endif 1501 1502 #if defined(__sun) || defined(sun) 1503 if (s.st_mode == S_IFLNK) 1504 #else 1505 if (fData->d_type == DT_UNKNOWN || fData->d_type == DT_LNK) 1506 #endif 1507 { 1508 if (stat(fData->d_name, &st) == 0) 1509 { 1510 fSize = (int64_t)st.st_size; 1511 searchRec->filesize = (fSize > INT32_MAX) ? -1 : (fSize & 0xFFFFFFFF); 1512 1513 if ((st.st_mode & S_IFMT) == S_IFDIR) 1514 searchRec->isDir = true; 1515 } 1516 } 1517 else if (!searchRec->isDir) 1518 { 1519 if (stat(fData->d_name, &st) == 0) 1520 { 1521 fSize = (int64_t)st.st_size; 1522 searchRec->filesize = (fSize > INT32_MAX) ? -1 : (fSize & 0xFFFFFFFF); 1523 } 1524 } 1525 #endif 1526 1527 if (searchRec->filesize < -1) 1528 searchRec->filesize = -1; 1529 1530 if (handleEntrySkip(searchRec->nameU, searchRec->isDir)) 1531 { 1532 // skip file 1533 free(searchRec->nameU); 1534 searchRec->nameU = NULL; 1535 1536 return LFF_SKIP; 1537 } 1538 1539 return LFF_OK; 1540 } 1541 1542 static void findClose(void) 1543 { 1544 if (hFind != NULL) 1545 { 1546 #ifdef _WIN32 1547 FindClose(hFind); 1548 #else 1549 closedir(hFind); 1550 #endif 1551 hFind = NULL; 1552 } 1553 } 1554 1555 static bool swapBufferEntry(int32_t a, int32_t b) // used for sorting 1556 { 1557 if (a >= FReq_FileCount || b >= FReq_FileCount) 1558 return false; 1559 1560 DirRec tmpBuffer = FReq_Buffer[a]; 1561 FReq_Buffer[a] = FReq_Buffer[b]; 1562 FReq_Buffer[b] = tmpBuffer; 1563 1564 return true; 1565 } 1566 1567 static char *ach(int32_t rad) // used for sortDirectory() 1568 { 1569 DirRec *dirEntry = &FReq_Buffer[rad]; 1570 1571 char *name = unicharToCp850(dirEntry->nameU, true); 1572 if (name == NULL) 1573 return NULL; 1574 1575 const int32_t nameLen = (int32_t)strlen(name); 1576 if (nameLen == 0) 1577 { 1578 free(name); 1579 return NULL; 1580 } 1581 1582 char *p = (char *)malloc(nameLen+1+1); 1583 if (p == NULL) 1584 { 1585 free(name); 1586 return NULL; 1587 } 1588 1589 if (dirEntry->isDir) 1590 { 1591 // directory 1592 1593 if (nameLen == 2 && name[0] == '.' && name[1] == '.') 1594 p[0] = 0x01; // make ".." directory first priority 1595 else 1596 p[0] = 0x02; // make second priority 1597 1598 strcpy(&p[1], name); 1599 1600 free(name); 1601 return p; 1602 } 1603 else 1604 { 1605 // file 1606 1607 const int32_t i = getExtOffset(name, nameLen); 1608 if (config.cfg_SortPriority == 1 || i == -1) 1609 { 1610 // sort by filename 1611 strcpy(p, name); 1612 free(name); 1613 return p; 1614 } 1615 else 1616 { 1617 // sort by filename extension 1618 const int32_t extLen = nameLen - i; 1619 if (extLen <= 1) 1620 { 1621 strcpy(p, name); 1622 free(name); 1623 return p; 1624 } 1625 1626 // FILENAME.EXT -> EXT.FILENAME (for sorting) 1627 memcpy(p, &name[i+1], extLen - 1); 1628 memcpy(&p[extLen-1], name, i); 1629 p[nameLen-1] = '\0'; 1630 1631 free(name); 1632 return p; 1633 } 1634 } 1635 } 1636 1637 static void sortDirectory(void) 1638 { 1639 bool didSwap; 1640 1641 if (FReq_FileCount < 2) 1642 return; // no need to sort 1643 1644 uint32_t offset = FReq_FileCount >> 1; 1645 while (offset > 0) 1646 { 1647 const uint32_t limit = FReq_FileCount - offset; 1648 do 1649 { 1650 didSwap = false; 1651 for (uint32_t i = 0; i < limit; i++) 1652 { 1653 char *p1 = ach(i); 1654 char *p2 = ach(offset+i); 1655 1656 if (p1 == NULL || p2 == NULL) 1657 { 1658 if (p1 != NULL) free(p1); 1659 if (p2 != NULL) free(p2); 1660 okBox(0, "System message", "Not enough memory!", NULL); 1661 return; 1662 } 1663 1664 if (_stricmp(p1, p2) > 0) 1665 { 1666 if (!swapBufferEntry(i, offset + i)) 1667 { 1668 free(p1); 1669 free(p2); 1670 return; 1671 } 1672 1673 didSwap = true; 1674 } 1675 1676 free(p1); 1677 free(p2); 1678 } 1679 } 1680 while (didSwap); 1681 1682 offset >>= 1; 1683 } 1684 } 1685 1686 static uint8_t numDigits32(uint32_t x) 1687 { 1688 if (x >= 1000000000) return 10; 1689 if (x >= 100000000) return 9; 1690 if (x >= 10000000) return 8; 1691 if (x >= 1000000) return 7; 1692 if (x >= 100000) return 6; 1693 if (x >= 10000) return 5; 1694 if (x >= 1000) return 4; 1695 if (x >= 100) return 3; 1696 if (x >= 10) return 2; 1697 1698 return 1; 1699 } 1700 1701 static void printFormattedFilesize(uint16_t x, uint16_t y, uint32_t bufEntry) 1702 { 1703 char sizeStrBuffer[16]; 1704 int32_t printFilesize; 1705 1706 const int32_t filesize = FReq_Buffer[bufEntry].filesize; 1707 if (filesize == -1) 1708 { 1709 x += 6; 1710 textOut(x, y, PAL_BLCKTXT, ">2GB"); 1711 return; 1712 } 1713 1714 assert(filesize >= 0); 1715 1716 if (filesize >= 1024*1024*10) // >= 10MB? 1717 { 1718 forceMB: 1719 printFilesize = (int32_t)ceil(filesize / (1024.0*1024.0)); 1720 x += (4 - numDigits32(printFilesize)) * (FONT1_CHAR_W - 1); 1721 sprintf(sizeStrBuffer, "%dM", printFilesize); 1722 } 1723 else if (filesize >= 1024*10) // >= 10kB? 1724 { 1725 printFilesize = (int32_t)ceil(filesize / 1024.0); 1726 if (printFilesize > 9999) 1727 goto forceMB; // ceil visual overflow kludge 1728 1729 x += (4 - numDigits32(printFilesize)) * (FONT1_CHAR_W - 1); 1730 sprintf(sizeStrBuffer, "%dk", printFilesize); 1731 } 1732 else // bytes 1733 { 1734 printFilesize = filesize; 1735 x += (5 - numDigits32(printFilesize)) * (FONT1_CHAR_W - 1); 1736 sprintf(sizeStrBuffer, "%d", printFilesize); 1737 } 1738 1739 textOut(x, y, PAL_BLCKTXT, sizeStrBuffer); 1740 } 1741 1742 static void displayCurrPath(void) 1743 { 1744 fillRect(4, 145, 162, 10, PAL_DESKTOP); 1745 1746 if (FReq_CurPathU == NULL) 1747 return; 1748 1749 const uint32_t pathLen = (uint32_t)UNICHAR_STRLEN(FReq_CurPathU); 1750 if (pathLen == 0) 1751 return; 1752 1753 char *asciiPath = unicharToCp850(FReq_CurPathU, true); 1754 if (asciiPath == NULL) 1755 { 1756 okBox(0, "System message", "Not enough memory!", NULL); 1757 return; 1758 } 1759 1760 char *p = asciiPath; 1761 if (textWidth(p) <= 162) 1762 { 1763 // path fits, print it all 1764 textOut(4, 145, PAL_FORGRND, p); 1765 } 1766 else 1767 { 1768 // path doesn't fit, print drive + ".." + last directory 1769 1770 #ifdef _WIN32 1771 memcpy(FReq_NameTemp, p, 3); // get drive (f.ex. C:\) 1772 FReq_NameTemp[3] = '\0'; 1773 1774 strcat(FReq_NameTemp, ".\001"); // special character in font 1775 FReq_NameTemp[5] = '\0'; 1776 #else 1777 FReq_NameTemp[0] = '\0'; 1778 strcpy(FReq_NameTemp, "/"); 1779 strcat(FReq_NameTemp, ".."); 1780 #endif 1781 1782 char *delimiter = strrchr(p, DIR_DELIMITER); 1783 if (delimiter != NULL) 1784 { 1785 #ifdef _WIN32 1786 strcat(FReq_NameTemp, delimiter+1); 1787 #else 1788 strcat(FReq_NameTemp, delimiter); 1789 #endif 1790 } 1791 1792 int32_t j = (int32_t)strlen(FReq_NameTemp); 1793 if (j > 6) 1794 { 1795 j--; 1796 1797 p = FReq_NameTemp; 1798 while (j >= 6 && textWidth(p) >= 162) 1799 { 1800 p[j-2] = '.'; 1801 p[j-1] = '.'; 1802 p[j-0] = '\0'; 1803 j--; 1804 } 1805 } 1806 1807 textOutClipX(4, 145, PAL_FORGRND, FReq_NameTemp, 165); 1808 } 1809 1810 free(asciiPath); 1811 } 1812 1813 void diskOp_DrawFilelist(void) 1814 { 1815 clearRect(FILENAME_TEXT_X-1, 4, 162, 164); 1816 1817 if (FReq_FileCount == 0) 1818 return; 1819 1820 // draw "selected file" rectangle 1821 if (FReq_EntrySelected != -1) 1822 { 1823 const uint16_t y = 4 + (uint16_t)((FONT1_CHAR_H + 1) * FReq_EntrySelected); 1824 fillRect(FILENAME_TEXT_X - 1, y, 162, FONT1_CHAR_H, PAL_PATTEXT); 1825 } 1826 1827 for (uint16_t i = 0; i < DISKOP_ENTRY_NUM; i++) 1828 { 1829 const int32_t bufEntry = FReq_DirPos + i; 1830 if (bufEntry >= FReq_FileCount) 1831 break; 1832 1833 if (FReq_Buffer[bufEntry].nameU == NULL) 1834 continue; 1835 1836 // convert unichar name to codepage 437 1837 char *readName = unicharToCp850(FReq_Buffer[bufEntry].nameU, true); 1838 if (readName == NULL) 1839 continue; 1840 1841 const uint16_t y = 4 + (i * (FONT1_CHAR_H + 1)); 1842 1843 // shrink entry name and add ".." if it doesn't fit on screen 1844 trimEntryName(readName, FReq_Buffer[bufEntry].isDir); 1845 1846 if (FReq_Buffer[bufEntry].isDir) 1847 { 1848 // directory 1849 charOut(FILENAME_TEXT_X, y, PAL_BLCKTXT, DIR_DELIMITER); 1850 textOut(FILENAME_TEXT_X + FONT1_CHAR_W, y, PAL_BLCKTXT, readName); 1851 } 1852 else 1853 { 1854 // filename 1855 textOut(FILENAME_TEXT_X, y, PAL_BLCKTXT, readName); 1856 } 1857 1858 free(readName); 1859 1860 if (!FReq_Buffer[bufEntry].isDir) 1861 printFormattedFilesize(FILESIZE_TEXT_X, y, bufEntry); 1862 } 1863 } 1864 1865 void diskOp_DrawDirectory(void) 1866 { 1867 drawTextBox(TB_DISKOP_FILENAME); 1868 1869 displayCurrPath(); 1870 #ifdef _WIN32 1871 setupDiskOpDrives(); 1872 #endif 1873 1874 setScrollBarEnd(SB_DISKOP_LIST, FReq_FileCount); 1875 setScrollBarPos(SB_DISKOP_LIST, FReq_DirPos, false); 1876 1877 diskOp_DrawFilelist(); 1878 } 1879 1880 static DirRec *bufferCreateEmptyDir(void) // special case: creates a dir entry with a ".." directory 1881 { 1882 DirRec *dirEntry = (DirRec *)malloc(sizeof (DirRec)); 1883 if (dirEntry == NULL) 1884 return NULL; 1885 1886 dirEntry->nameU = UNICHAR_STRDUP(PARENT_DIR_STR); 1887 if (dirEntry->nameU == NULL) 1888 { 1889 free(dirEntry); 1890 return NULL; 1891 } 1892 1893 dirEntry->isDir = true; 1894 dirEntry->filesize = 0; 1895 1896 return dirEntry; 1897 } 1898 1899 static int32_t SDLCALL diskOp_ReadDirectoryThread(void *ptr) 1900 { 1901 DirRec tmpBuffer; 1902 1903 FReq_DirPos = 0; 1904 1905 // free old buffer 1906 freeDirRecBuffer(); 1907 1908 UNICHAR_GETCWD(FReq_CurPathU, PATH_MAX); 1909 1910 // read first file 1911 int8_t lastFindFileFlag = findFirst(&tmpBuffer); 1912 if (lastFindFileFlag != LFF_DONE && lastFindFileFlag != LFF_SKIP) 1913 { 1914 FReq_Buffer = (DirRec *)malloc(sizeof (DirRec) * (FReq_FileCount+1)); 1915 if (FReq_Buffer == NULL) 1916 { 1917 findClose(); 1918 1919 okBoxThreadSafe(0, "System message", "Not enough memory!", NULL); 1920 1921 FReq_Buffer = bufferCreateEmptyDir(); 1922 if (FReq_Buffer != NULL) 1923 FReq_FileCount = 1; 1924 else 1925 okBoxThreadSafe(0, "System message", "Not enough memory!", NULL); 1926 1927 setMouseBusy(false); 1928 return false; 1929 } 1930 1931 memcpy(&FReq_Buffer[FReq_FileCount], &tmpBuffer, sizeof (DirRec)); 1932 FReq_FileCount++; 1933 } 1934 1935 // read remaining files 1936 while (lastFindFileFlag != LFF_DONE) 1937 { 1938 lastFindFileFlag = findNext(&tmpBuffer); 1939 if (lastFindFileFlag != LFF_DONE && lastFindFileFlag != LFF_SKIP) 1940 { 1941 DirRec *newPtr = (DirRec *)realloc(FReq_Buffer, sizeof (DirRec) * (FReq_FileCount + 1)); 1942 if (newPtr == NULL) 1943 { 1944 freeDirRecBuffer(); 1945 okBoxThreadSafe(0, "System message", "Not enough memory!", NULL); 1946 break; 1947 } 1948 1949 FReq_Buffer = newPtr; 1950 1951 memcpy(&FReq_Buffer[FReq_FileCount], &tmpBuffer, sizeof (DirRec)); 1952 FReq_FileCount++; 1953 } 1954 } 1955 1956 findClose(); 1957 1958 if (FReq_FileCount > 0) 1959 { 1960 sortDirectory(); 1961 } 1962 else 1963 { 1964 // access denied or out of memory - create parent directory link 1965 FReq_Buffer = bufferCreateEmptyDir(); 1966 if (FReq_Buffer != NULL) 1967 FReq_FileCount = 1; 1968 else 1969 okBoxThreadSafe(0, "System message", "Not enough memory!", NULL); 1970 } 1971 1972 editor.diskOpReadDone = true; 1973 setMouseBusy(false); 1974 1975 return true; 1976 1977 (void)ptr; 1978 } 1979 1980 void diskOp_StartDirReadThread(void) 1981 { 1982 editor.diskOpReadDone = false; 1983 1984 mouseAnimOn(); 1985 thread = SDL_CreateThread(diskOp_ReadDirectoryThread, NULL, NULL); 1986 if (thread == NULL) 1987 { 1988 editor.diskOpReadDone = true; 1989 okBox(0, "System message", "Couldn't create thread!", NULL); 1990 return; 1991 } 1992 1993 SDL_DetachThread(thread); 1994 } 1995 1996 static void drawSaveAsElements(void) 1997 { 1998 switch (FReq_Item) 1999 { 2000 default: 2001 case DISKOP_ITEM_MODULE: 2002 { 2003 textOutShadow(19, 101, PAL_FORGRND, PAL_DSKTOP2, "MOD"); 2004 textOutShadow(19, 115, PAL_FORGRND, PAL_DSKTOP2, "XM"); 2005 textOutShadow(19, 129, PAL_FORGRND, PAL_DSKTOP2, "WAV"); 2006 } 2007 break; 2008 2009 case DISKOP_ITEM_INSTR: 2010 textOutShadow(19, 101, PAL_FORGRND, PAL_DSKTOP2, "XI"); 2011 break; 2012 2013 case DISKOP_ITEM_SAMPLE: 2014 { 2015 textOutShadow(19, 101, PAL_FORGRND, PAL_DSKTOP2, "RAW"); 2016 textOutShadow(19, 115, PAL_FORGRND, PAL_DSKTOP2, "IFF"); 2017 textOutShadow(19, 129, PAL_FORGRND, PAL_DSKTOP2, "WAV"); 2018 } 2019 break; 2020 2021 case DISKOP_ITEM_PATTERN: 2022 textOutShadow(19, 101, PAL_FORGRND, PAL_DSKTOP2, "XP"); 2023 break; 2024 2025 case DISKOP_ITEM_TRACK: 2026 textOutShadow(19, 101, PAL_FORGRND, PAL_DSKTOP2, "XT"); 2027 break; 2028 } 2029 } 2030 2031 static void setDiskOpItemRadioButtons(void) 2032 { 2033 uncheckRadioButtonGroup(RB_GROUP_DISKOP_MOD_SAVEAS); 2034 uncheckRadioButtonGroup(RB_GROUP_DISKOP_INS_SAVEAS); 2035 uncheckRadioButtonGroup(RB_GROUP_DISKOP_SMP_SAVEAS); 2036 uncheckRadioButtonGroup(RB_GROUP_DISKOP_PAT_SAVEAS); 2037 uncheckRadioButtonGroup(RB_GROUP_DISKOP_TRK_SAVEAS); 2038 2039 hideRadioButtonGroup(RB_GROUP_DISKOP_MOD_SAVEAS); 2040 hideRadioButtonGroup(RB_GROUP_DISKOP_INS_SAVEAS); 2041 hideRadioButtonGroup(RB_GROUP_DISKOP_SMP_SAVEAS); 2042 hideRadioButtonGroup(RB_GROUP_DISKOP_PAT_SAVEAS); 2043 hideRadioButtonGroup(RB_GROUP_DISKOP_TRK_SAVEAS); 2044 2045 if (editor.moduleSaveMode > 3) 2046 editor.moduleSaveMode = 3; 2047 2048 if (editor.sampleSaveMode > 3) 2049 editor.sampleSaveMode = 3; 2050 2051 radioButtons[RB_DISKOP_MOD_SAVEAS_MOD + editor.moduleSaveMode].state = RADIOBUTTON_CHECKED; 2052 radioButtons[RB_DISKOP_SMP_SAVEAS_RAW + editor.sampleSaveMode].state = RADIOBUTTON_CHECKED; 2053 2054 if (FReq_Item == DISKOP_ITEM_INSTR) radioButtons[RB_DISKOP_INS_SAVEAS_XI].state = RADIOBUTTON_CHECKED; 2055 if (FReq_Item == DISKOP_ITEM_PATTERN) radioButtons[RB_DISKOP_PAT_SAVEAS_XP].state = RADIOBUTTON_CHECKED; 2056 if (FReq_Item == DISKOP_ITEM_TRACK) radioButtons[RB_DISKOP_TRK_SAVEAS_XT].state = RADIOBUTTON_CHECKED; 2057 2058 if (ui.diskOpShown) 2059 { 2060 switch (FReq_Item) 2061 { 2062 default: case DISKOP_ITEM_MODULE: showRadioButtonGroup(RB_GROUP_DISKOP_MOD_SAVEAS); break; 2063 case DISKOP_ITEM_INSTR: showRadioButtonGroup(RB_GROUP_DISKOP_INS_SAVEAS); break; 2064 case DISKOP_ITEM_SAMPLE: showRadioButtonGroup(RB_GROUP_DISKOP_SMP_SAVEAS); break; 2065 case DISKOP_ITEM_PATTERN: showRadioButtonGroup(RB_GROUP_DISKOP_PAT_SAVEAS); break; 2066 case DISKOP_ITEM_TRACK: showRadioButtonGroup(RB_GROUP_DISKOP_TRK_SAVEAS); break; 2067 } 2068 } 2069 } 2070 2071 static void setDiskOpItem(uint8_t item) 2072 { 2073 hideRadioButtonGroup(RB_GROUP_DISKOP_MOD_SAVEAS); 2074 hideRadioButtonGroup(RB_GROUP_DISKOP_INS_SAVEAS); 2075 hideRadioButtonGroup(RB_GROUP_DISKOP_SMP_SAVEAS); 2076 hideRadioButtonGroup(RB_GROUP_DISKOP_PAT_SAVEAS); 2077 hideRadioButtonGroup(RB_GROUP_DISKOP_TRK_SAVEAS); 2078 2079 if (item > 4) 2080 item = 4; 2081 2082 FReq_Item = item; 2083 switch (FReq_Item) 2084 { 2085 default: 2086 case DISKOP_ITEM_MODULE: 2087 { 2088 FReq_FileName = modTmpFName; 2089 2090 // FReq_ModCurPathU is always set at this point 2091 2092 FReq_CurPathU = FReq_ModCurPathU; 2093 if (FReq_CurPathU != NULL && FReq_CurPathU[0] != '\0') 2094 UNICHAR_CHDIR(FReq_CurPathU); 2095 } 2096 break; 2097 2098 case DISKOP_ITEM_INSTR: 2099 { 2100 FReq_FileName = insTmpFName; 2101 2102 if (!insPathSet && FReq_CurPathU != NULL && FReq_CurPathU[0] != '\0') 2103 { 2104 UNICHAR_STRCPY(FReq_InsCurPathU, FReq_CurPathU); 2105 insPathSet = true; 2106 } 2107 2108 FReq_CurPathU = FReq_InsCurPathU; 2109 if (FReq_CurPathU != NULL) 2110 UNICHAR_CHDIR(FReq_CurPathU); 2111 } 2112 break; 2113 2114 case DISKOP_ITEM_SAMPLE: 2115 { 2116 FReq_FileName = smpTmpFName; 2117 2118 if (!smpPathSet && FReq_CurPathU != NULL && FReq_CurPathU[0] != '\0') 2119 { 2120 UNICHAR_STRCPY(FReq_SmpCurPathU, FReq_CurPathU); 2121 smpPathSet = true; 2122 } 2123 2124 FReq_CurPathU = FReq_SmpCurPathU; 2125 if (FReq_CurPathU != NULL) 2126 UNICHAR_CHDIR(FReq_CurPathU); 2127 } 2128 break; 2129 2130 case DISKOP_ITEM_PATTERN: 2131 { 2132 FReq_FileName = patTmpFName; 2133 2134 if (!patPathSet && FReq_CurPathU != NULL && FReq_CurPathU[0] != '\0') 2135 { 2136 UNICHAR_STRCPY(FReq_PatCurPathU, FReq_CurPathU); 2137 patPathSet = true; 2138 } 2139 2140 FReq_CurPathU = FReq_PatCurPathU; 2141 if (FReq_CurPathU != NULL) 2142 UNICHAR_CHDIR(FReq_CurPathU); 2143 } 2144 break; 2145 2146 case DISKOP_ITEM_TRACK: 2147 { 2148 FReq_FileName = trkTmpFName; 2149 2150 if (!trkPathSet && FReq_CurPathU != NULL && FReq_CurPathU[0] != '\0') 2151 { 2152 UNICHAR_STRCPY(FReq_TrkCurPathU, FReq_CurPathU); 2153 trkPathSet = true; 2154 } 2155 2156 FReq_CurPathU = FReq_TrkCurPathU; 2157 if (FReq_CurPathU != NULL) 2158 UNICHAR_CHDIR(FReq_CurPathU); 2159 } 2160 break; 2161 } 2162 2163 if (FReq_CurPathU != NULL && FReq_ModCurPathU != NULL) 2164 { 2165 if (FReq_CurPathU[0] == '\0' && FReq_ModCurPathU[0] != '\0') 2166 UNICHAR_STRCPY(FReq_CurPathU, FReq_ModCurPathU); 2167 } 2168 2169 textBoxes[TB_DISKOP_FILENAME].textPtr = FReq_FileName; 2170 FReq_ShowAllFiles = false; 2171 2172 if (ui.diskOpShown) 2173 { 2174 editor.diskOpReadDir = true; 2175 2176 fillRect(4, 101, 40, 38, PAL_DESKTOP); 2177 drawSaveAsElements(); 2178 setDiskOpItemRadioButtons(); 2179 2180 diskOp_DrawDirectory(); 2181 drawTextBox(TB_DISKOP_FILENAME); 2182 } 2183 else 2184 { 2185 editor.diskOpReadOnOpen = true; 2186 } 2187 } 2188 2189 static void drawDiskOpScreen(void) 2190 { 2191 drawFramework(0, 0, 67, 86, FRAMEWORK_TYPE1); 2192 drawFramework(67, 0, 64, 142, FRAMEWORK_TYPE1); 2193 drawFramework(131, 0, 37, 142, FRAMEWORK_TYPE1); 2194 drawFramework(0, 86, 67, 56, FRAMEWORK_TYPE1); 2195 drawFramework(0, 142, 168, 31, FRAMEWORK_TYPE1); 2196 drawFramework(168, 0, 164, 3, FRAMEWORK_TYPE1); 2197 drawFramework(168, 170, 164, 3, FRAMEWORK_TYPE1); 2198 drawFramework(332, 0, 24, 173, FRAMEWORK_TYPE1); 2199 drawFramework(30, 157, 136, 14, FRAMEWORK_TYPE2); 2200 2201 clearRect(168, 2, 164, 168); 2202 2203 showPushButton(PB_DISKOP_SAVE); 2204 showPushButton(PB_DISKOP_DELETE); 2205 showPushButton(PB_DISKOP_RENAME); 2206 showPushButton(PB_DISKOP_MAKEDIR); 2207 showPushButton(PB_DISKOP_REFRESH); 2208 showPushButton(PB_DISKOP_EXIT); 2209 showPushButton(PB_DISKOP_PARENT); 2210 showPushButton(PB_DISKOP_ROOT); 2211 showPushButton(PB_DISKOP_SHOW_ALL); 2212 showPushButton(PB_DISKOP_SET_PATH); 2213 showPushButton(PB_DISKOP_LIST_UP); 2214 showPushButton(PB_DISKOP_LIST_DOWN); 2215 2216 showScrollBar(SB_DISKOP_LIST); 2217 showTextBox(TB_DISKOP_FILENAME); 2218 2219 textBoxes[TB_DISKOP_FILENAME].textPtr = FReq_FileName; 2220 2221 if (FReq_Item > 4) 2222 FReq_Item = 4; 2223 2224 uncheckRadioButtonGroup(RB_GROUP_DISKOP_ITEM); 2225 radioButtons[RB_DISKOP_MODULE + FReq_Item].state = RADIOBUTTON_CHECKED; 2226 showRadioButtonGroup(RB_GROUP_DISKOP_ITEM); 2227 2228 // item selector 2229 textOutShadow(5, 3, PAL_FORGRND, PAL_DSKTOP2, "Item:"); 2230 textOutShadow(19, 17, PAL_FORGRND, PAL_DSKTOP2, "Module"); 2231 textOutShadow(19, 31, PAL_FORGRND, PAL_DSKTOP2, "Instr."); 2232 textOutShadow(19, 45, PAL_FORGRND, PAL_DSKTOP2, "Sample"); 2233 textOutShadow(19, 59, PAL_FORGRND, PAL_DSKTOP2, "Pattern"); 2234 textOutShadow(19, 73, PAL_FORGRND, PAL_DSKTOP2, "Track"); 2235 2236 // file format 2237 textOutShadow(5, 89, PAL_FORGRND, PAL_DSKTOP2, "Save as:"); 2238 drawSaveAsElements(); 2239 setDiskOpItemRadioButtons(); 2240 2241 // filename 2242 textOutShadow(4, 159, PAL_FORGRND, PAL_DSKTOP2, "File:"); 2243 2244 diskOp_DrawDirectory(); 2245 } 2246 2247 void showDiskOpScreen(void) 2248 { 2249 // if first time opening Disk Op., set initial directory 2250 if (firstTimeOpeningDiskOp) 2251 { 2252 assert(FReq_ModCurPathU != NULL); 2253 2254 // first test if we can change the dir to the one stored in the config (if present) 2255 if (FReq_ModCurPathU[0] == '\0' || UNICHAR_CHDIR(FReq_ModCurPathU) != 0) 2256 { 2257 // nope, couldn't do that, set Disk Op. path to the user's desktop directory 2258 #ifdef _WIN32 2259 SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY, NULL, 0, FReq_ModCurPathU); 2260 #else 2261 char *home = getenv("HOME"); 2262 if (home != NULL) 2263 { 2264 UNICHAR_CHDIR(home); 2265 UNICHAR_STRCPY(FReq_ModCurPathU, home); 2266 2267 UNICHAR_STRCAT(FReq_ModCurPathU, "/Desktop"); 2268 } 2269 #endif 2270 UNICHAR_CHDIR(FReq_ModCurPathU); 2271 } 2272 2273 UNICHAR_GETCWD(FReq_ModCurPathU, PATH_MAX); 2274 firstTimeOpeningDiskOp = false; 2275 } 2276 2277 if (ui.extendedPatternEditor) 2278 exitPatternEditorExtended(); 2279 2280 hideTopScreen(); 2281 ui.diskOpShown = true; 2282 ui.scopesShown = false; 2283 2284 showTopRightMainScreen(); 2285 drawDiskOpScreen(); 2286 2287 // a flag that says if we need to update the filelist after disk op. was shown 2288 if (editor.diskOpReadOnOpen) 2289 { 2290 editor.diskOpReadOnOpen = false; 2291 diskOp_StartDirReadThread(); 2292 } 2293 } 2294 2295 void hideDiskOpScreen(void) 2296 { 2297 #ifdef _WIN32 2298 for (uint16_t i = 0; i < DISKOP_MAX_DRIVE_BUTTONS; i++) 2299 hidePushButton(PB_DISKOP_DRIVE1 + i); 2300 #endif 2301 2302 hidePushButton(PB_DISKOP_SAVE); 2303 hidePushButton(PB_DISKOP_DELETE); 2304 hidePushButton(PB_DISKOP_RENAME); 2305 hidePushButton(PB_DISKOP_MAKEDIR); 2306 hidePushButton(PB_DISKOP_REFRESH); 2307 hidePushButton(PB_DISKOP_EXIT); 2308 hidePushButton(PB_DISKOP_PARENT); 2309 hidePushButton(PB_DISKOP_ROOT); 2310 hidePushButton(PB_DISKOP_SHOW_ALL); 2311 hidePushButton(PB_DISKOP_SET_PATH); 2312 hidePushButton(PB_DISKOP_LIST_UP); 2313 hidePushButton(PB_DISKOP_LIST_DOWN); 2314 2315 hideScrollBar(SB_DISKOP_LIST); 2316 hideTextBox(TB_DISKOP_FILENAME); 2317 hideRadioButtonGroup(RB_GROUP_DISKOP_ITEM); 2318 hideRadioButtonGroup(RB_GROUP_DISKOP_MOD_SAVEAS); 2319 hideRadioButtonGroup(RB_GROUP_DISKOP_INS_SAVEAS); 2320 hideRadioButtonGroup(RB_GROUP_DISKOP_SMP_SAVEAS); 2321 hideRadioButtonGroup(RB_GROUP_DISKOP_PAT_SAVEAS); 2322 hideRadioButtonGroup(RB_GROUP_DISKOP_TRK_SAVEAS); 2323 2324 ui.diskOpShown = false; 2325 } 2326 2327 void exitDiskOpScreen(void) 2328 { 2329 hideDiskOpScreen(); 2330 ui.oldTopLeftScreen = 0; // disk op. ignores previously opened top screens 2331 showTopScreen(true); 2332 } 2333 2334 void toggleDiskOpScreen(void) 2335 { 2336 if (ui.diskOpShown) 2337 exitDiskOpScreen(); 2338 else 2339 showDiskOpScreen(); 2340 } 2341 2342 void sbDiskOpSetPos(uint32_t pos) 2343 { 2344 if ((int32_t)pos != FReq_DirPos && FReq_FileCount > DISKOP_ENTRY_NUM) 2345 { 2346 FReq_DirPos = (int32_t)pos; 2347 diskOp_DrawFilelist(); 2348 } 2349 } 2350 2351 void pbDiskOpListUp(void) 2352 { 2353 if (FReq_DirPos > 0 && FReq_FileCount > DISKOP_ENTRY_NUM) 2354 scrollBarScrollUp(SB_DISKOP_LIST, 1); 2355 } 2356 2357 void pbDiskOpListDown(void) 2358 { 2359 if (FReq_DirPos < FReq_FileCount-DISKOP_ENTRY_NUM && FReq_FileCount > DISKOP_ENTRY_NUM) 2360 scrollBarScrollDown(SB_DISKOP_LIST, 1); 2361 } 2362 2363 void pbDiskOpParent(void) 2364 { 2365 diskOpGoParent(); 2366 } 2367 2368 void pbDiskOpRoot(void) 2369 { 2370 #ifdef _WIN32 2371 openDirectory(L"\\"); 2372 #else 2373 openDirectory("/"); 2374 #endif 2375 } 2376 2377 void pbDiskOpShowAll(void) 2378 { 2379 FReq_ShowAllFiles = true; 2380 editor.diskOpReadDir = true; // refresh dir 2381 } 2382 2383 #ifdef _WIN32 2384 void pbDiskOpDrive1(void) { openDrive(logicalDriveNames[driveIndexes[0]]); } 2385 void pbDiskOpDrive2(void) { openDrive(logicalDriveNames[driveIndexes[1]]); } 2386 void pbDiskOpDrive3(void) { openDrive(logicalDriveNames[driveIndexes[2]]); } 2387 void pbDiskOpDrive4(void) { openDrive(logicalDriveNames[driveIndexes[3]]); } 2388 void pbDiskOpDrive5(void) { openDrive(logicalDriveNames[driveIndexes[4]]); } 2389 void pbDiskOpDrive6(void) { openDrive(logicalDriveNames[driveIndexes[5]]); } 2390 void pbDiskOpDrive7(void) { openDrive(logicalDriveNames[driveIndexes[6]]); } 2391 void pbDiskOpDrive8(void) { openDrive(logicalDriveNames[driveIndexes[7]]); } 2392 #endif 2393 2394 void pbDiskOpDelete(void) 2395 { 2396 setMouseMode(MOUSE_MODE_DELETE); 2397 } 2398 2399 void pbDiskOpRename(void) 2400 { 2401 setMouseMode(MOUSE_MODE_RENAME); 2402 } 2403 2404 void pbDiskOpMakeDir(void) 2405 { 2406 FReq_NameTemp[0] = '\0'; 2407 if (inputBox(1, "Enter directory name:", FReq_NameTemp, PATH_MAX) == 1) 2408 { 2409 if (FReq_NameTemp[0] == '\0') 2410 { 2411 okBox(0, "System message", "Name can't be empty!", NULL); 2412 return; 2413 } 2414 2415 if (makeDirAnsi(FReq_NameTemp)) 2416 editor.diskOpReadDir = true; 2417 else 2418 okBox(0, "System message", "Couldn't create directory: Access denied, or a dir with the same name already exists!", NULL); 2419 } 2420 } 2421 2422 void pbDiskOpRefresh(void) 2423 { 2424 editor.diskOpReadDir = true; // refresh dir 2425 #ifdef _WIN32 2426 setupDiskOpDrives(); 2427 #endif 2428 } 2429 2430 void pbDiskOpSetPath(void) 2431 { 2432 FReq_NameTemp[0] = '\0'; 2433 if (inputBox(1, "Enter new directory path:", FReq_NameTemp, PATH_MAX) == 1) 2434 { 2435 if (FReq_NameTemp[0] == '\0') 2436 { 2437 okBox(0, "System message", "Name can't be empty!", NULL); 2438 return; 2439 } 2440 2441 if (chdir(FReq_NameTemp) == 0) 2442 editor.diskOpReadDir = true; 2443 else 2444 okBox(0, "System message", "Couldn't set directory path!", NULL); 2445 } 2446 } 2447 2448 void pbDiskOpExit(void) 2449 { 2450 exitDiskOpScreen(); 2451 } 2452 2453 void rbDiskOpModule(void) 2454 { 2455 checkRadioButton(RB_DISKOP_MODULE); 2456 setDiskOpItem(DISKOP_ITEM_MODULE); 2457 } 2458 2459 void rbDiskOpInstr(void) 2460 { 2461 checkRadioButton(RB_DISKOP_INSTR); 2462 setDiskOpItem(DISKOP_ITEM_INSTR); 2463 } 2464 2465 void rbDiskOpSample(void) 2466 { 2467 checkRadioButton(RB_DISKOP_SAMPLE); 2468 setDiskOpItem(DISKOP_ITEM_SAMPLE); 2469 } 2470 2471 void rbDiskOpPattern(void) 2472 { 2473 checkRadioButton(RB_DISKOP_PATTERN); 2474 setDiskOpItem(DISKOP_ITEM_PATTERN); 2475 } 2476 2477 void rbDiskOpTrack(void) 2478 { 2479 checkRadioButton(RB_DISKOP_TRACK); 2480 setDiskOpItem(DISKOP_ITEM_TRACK); 2481 } 2482 2483 void rbDiskOpModSaveMod(void) 2484 { 2485 if (editor.moduleSaveMode == MOD_SAVE_MODE_WAV) 2486 editor.diskOpReadDir = true; 2487 2488 editor.moduleSaveMode = MOD_SAVE_MODE_MOD; 2489 checkRadioButton(RB_DISKOP_MOD_SAVEAS_MOD); 2490 diskOpChangeFilenameExt(".mod"); 2491 2492 updateCurrSongFilename(); // for window title 2493 updateWindowTitle(true); 2494 } 2495 2496 void rbDiskOpModSaveXm(void) 2497 { 2498 if (editor.moduleSaveMode == MOD_SAVE_MODE_WAV) 2499 editor.diskOpReadDir = true; 2500 2501 editor.moduleSaveMode = MOD_SAVE_MODE_XM; 2502 checkRadioButton(RB_DISKOP_MOD_SAVEAS_XM); 2503 diskOpChangeFilenameExt(".xm"); 2504 2505 updateCurrSongFilename(); // for window title 2506 updateWindowTitle(true); 2507 } 2508 2509 void rbDiskOpModSaveWav(void) 2510 { 2511 editor.moduleSaveMode = MOD_SAVE_MODE_WAV; 2512 checkRadioButton(RB_DISKOP_MOD_SAVEAS_WAV); 2513 diskOpChangeFilenameExt(".wav"); 2514 2515 updateCurrSongFilename(); // for window title 2516 updateWindowTitle(true); 2517 2518 editor.diskOpReadDir = true; 2519 } 2520 2521 void rbDiskOpSmpSaveRaw(void) 2522 { 2523 editor.sampleSaveMode = SMP_SAVE_MODE_RAW; 2524 checkRadioButton(RB_DISKOP_SMP_SAVEAS_RAW); 2525 diskOpChangeFilenameExt(".raw"); 2526 } 2527 2528 void rbDiskOpSmpSaveIff(void) 2529 { 2530 editor.sampleSaveMode = SMP_SAVE_MODE_IFF; 2531 checkRadioButton(RB_DISKOP_SMP_SAVEAS_IFF); 2532 diskOpChangeFilenameExt(".iff"); 2533 } 2534 2535 void rbDiskOpSmpSaveWav(void) 2536 { 2537 editor.sampleSaveMode = SMP_SAVE_MODE_WAV; 2538 checkRadioButton(RB_DISKOP_SMP_SAVEAS_WAV); 2539 diskOpChangeFilenameExt(".wav"); 2540 }