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