1 // for finding memory leaks in debug mode with Visual Studio
2 #if defined _DEBUG && defined _MSC_VER
3 #include <crtdbg.h>
4 #endif
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <stdint.h>
9 #include <stdbool.h>
10 #include <math.h>
11 #ifdef _WIN32
12 #include <direct.h>
13 #include <io.h>
14 #define WIN32_MEAN_AND_LEAN
15 #include <windows.h>
16 #include <shlobj.h> // SHGetFolderPathW()
17 #else
18 #include <unistd.h>
19 #include <dirent.h>
20 #endif
21 #include <fcntl.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <time.h>
25 #include <limits.h>
26 #include "pt2_header.h"
27 #include "pt2_textout.h"
28 #include "pt2_diskop.h"
29 #include "pt2_tables.h"
30 #include "pt2_module_loader.h"
31 #include "pt2_audio.h"
32 #include "pt2_sampler.h"
33 #include "pt2_config.h"
34 #include "pt2_helpers.h"
35 #include "pt2_keyboard.h"
36 #include "pt2_visuals.h"
37 #include "pt2_sample_loader.h"
38 #include "pt2_bmp.h"
39
40 typedef struct fileEntry_t
41 {
42 UNICHAR *nameU;
43 char firstAnsiChar; // for handleEntryJumping();
44 char dateChanged[6 + 1];
45 bool isDir;
46 int32_t filesize;
47 } fileEntry_t;
48
49 // "look for file" flags
50 enum
51 {
52 LFF_DONE = 0,
53 LFF_SKIP = 1,
54 LFF_OK = 2
55 };
56
57 #ifdef _WIN32
58 #define PARENT_DIR_STR L".."
59 static HANDLE hFind;
60 #else
61 #define PARENT_DIR_STR ".."
62 static DIR *hFind;
63 #endif
64
65 static char fileNameBuffer[PATH_MAX + 1];
66 static uint32_t oldFileEntryRow;
67 static UNICHAR pathTmp[PATH_MAX + 2];
68 static fileEntry_t *diskOpEntry;
69
addSampleFileExt(char * fileName)70 void addSampleFileExt(char *fileName)
71 {
72 switch (diskop.smpSaveType)
73 {
74 case DISKOP_SMP_WAV: strcat(fileName, ".wav"); break;
75 case DISKOP_SMP_IFF: strcat(fileName, ".iff"); break;
76 default: case DISKOP_SMP_RAW: break;
77 }
78 }
79
bufferCreateEmptyDir(void)80 static fileEntry_t *bufferCreateEmptyDir(void) // special case: creates a dir entry with a ".." directory
81 {
82 fileEntry_t *dirEntry;
83
84 dirEntry = (fileEntry_t *)malloc(sizeof (fileEntry_t));
85 if (dirEntry == NULL)
86 return NULL;
87
88 dirEntry->nameU = UNICHAR_STRDUP(PARENT_DIR_STR);
89 if (dirEntry->nameU == NULL)
90 {
91 free(dirEntry);
92 return NULL;
93 }
94
95 dirEntry->isDir = true;
96 dirEntry->filesize = 0;
97
98 return dirEntry;
99 }
100
101 // deciding whether to load this entry into the buffer or not
listEntry(fileEntry_t * f)102 static bool listEntry(fileEntry_t *f)
103 {
104 int32_t entryName = (int32_t)UNICHAR_STRLEN(f->nameU);
105
106 #ifdef _WIN32
107 if (f->isDir && entryName == 2 && !UNICHAR_STRNICMP(f->nameU, "..", 2))
108 return true; // always show ".." directory
109 #else
110 if (f->isDir && entryName == 2 && !UNICHAR_STRNICMP(f->nameU, "..", 2) && !(editor.currPath[0] == '/' && editor.currPath[1] == '\0'))
111 return true; // always show ".." directory (unless in root dir)
112 #endif
113
114 if (!UNICHAR_STRNICMP(f->nameU, ".", 1))
115 return false; // skip ".name" entries
116
117 if (f->isDir || diskop.mode == DISKOP_MODE_SMP)
118 return true; // list all entries
119
120 if (entryName >= 4)
121 {
122 if (!UNICHAR_STRNICMP(f->nameU, "MOD.", 4) || !UNICHAR_STRNICMP(&f->nameU[entryName - 4], ".MOD", 4) ||
123 !UNICHAR_STRNICMP(f->nameU, "STK.", 4) || !UNICHAR_STRNICMP(&f->nameU[entryName - 4], ".STK", 4) ||
124 !UNICHAR_STRNICMP(f->nameU, "M15.", 4) || !UNICHAR_STRNICMP(&f->nameU[entryName - 4], ".M15", 4) ||
125 !UNICHAR_STRNICMP(f->nameU, "NST.", 4) || !UNICHAR_STRNICMP(&f->nameU[entryName - 4], ".NST", 4) ||
126 !UNICHAR_STRNICMP(f->nameU, "UST.", 4) || !UNICHAR_STRNICMP(&f->nameU[entryName - 4], ".UST", 4) ||
127 !UNICHAR_STRNICMP(f->nameU, "PP.", 3) || !UNICHAR_STRNICMP(&f->nameU[entryName - 3], ".PP", 3) ||
128 !UNICHAR_STRNICMP(f->nameU, "NT.", 3) || !UNICHAR_STRNICMP(&f->nameU[entryName - 3], ".NT", 3))
129 {
130 return true;
131 }
132 }
133
134 return false;
135 }
136
findFirst(fileEntry_t * searchRec)137 static int8_t findFirst(fileEntry_t *searchRec)
138 {
139 #ifdef _WIN32
140 WIN32_FIND_DATAW fData;
141 SYSTEMTIME sysTime;
142 #else
143 struct dirent *fData;
144 struct stat st;
145 int64_t fSize;
146 #endif
147
148 searchRec->nameU = NULL; // this one must be initialized
149
150 #ifdef _WIN32
151 hFind = FindFirstFileW(L"*", &fData);
152 if (hFind == NULL || hFind == INVALID_HANDLE_VALUE)
153 return LFF_DONE;
154
155 searchRec->nameU = UNICHAR_STRDUP(fData.cFileName);
156 if (searchRec->nameU == NULL)
157 return LFF_SKIP;
158
159 searchRec->filesize = (fData.nFileSizeHigh > 0) ? -1 : fData.nFileSizeLow;
160 searchRec->isDir = (fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false;
161 #else
162 hFind = opendir(".");
163 if (hFind == NULL)
164 return LFF_DONE;
165
166 fData = readdir(hFind);
167 if (fData == NULL)
168 return LFF_DONE;
169
170 searchRec->nameU = UNICHAR_STRDUP(fData->d_name);
171 if (searchRec->nameU == NULL)
172 return LFF_SKIP;
173
174 searchRec->filesize = 0;
175 searchRec->isDir = (fData->d_type == DT_DIR) ? true : false;
176
177 if (fData->d_type == DT_UNKNOWN || fData->d_type == DT_LNK)
178 {
179 if (stat(fData->d_name, &st) == 0)
180 {
181 fSize = (int64_t)st.st_size;
182 searchRec->filesize = (fSize > INT32_MAX) ? -1 : (fSize & 0xFFFFFFFF);
183
184 if ((st.st_mode & S_IFMT) == S_IFDIR)
185 searchRec->isDir = true;
186 }
187 }
188 else if (!searchRec->isDir)
189 {
190 if (stat(fData->d_name, &st) == 0)
191 {
192 fSize = (int64_t)st.st_size;
193 searchRec->filesize = (fSize > INT32_MAX) ? -1 : (fSize & 0xFFFFFFFF);
194 }
195 }
196 #endif
197
198 if (searchRec->filesize < -1)
199 searchRec->filesize = -1;
200
201 if (!listEntry(searchRec))
202 {
203 // skip entry
204 free(searchRec->nameU);
205 searchRec->nameU = NULL;
206 return LFF_SKIP;
207 }
208
209 #ifdef _WIN32
210 FileTimeToSystemTime(&fData.ftLastWriteTime, &sysTime);
211 snprintf(searchRec->dateChanged, 7, "%02d%02d%02d", sysTime.wDay, sysTime.wMonth, sysTime.wYear % 100);
212 unicharToAnsi(&searchRec->firstAnsiChar, searchRec->nameU, 1);
213 #else
214 strftime(searchRec->dateChanged, 7, "%d%m%y", localtime(&st.st_mtime));
215 searchRec->firstAnsiChar = (char)searchRec->nameU[0];
216 #endif
217
218 return LFF_OK;
219 }
220
findNext(fileEntry_t * searchRec)221 static int8_t findNext(fileEntry_t *searchRec)
222 {
223 #ifdef _WIN32
224 WIN32_FIND_DATAW fData;
225 SYSTEMTIME sysTime;
226 #else
227 struct dirent *fData;
228 struct stat st;
229 int64_t fSize;
230 #endif
231
232 searchRec->nameU = NULL; // important
233
234 #ifdef _WIN32
235 if (hFind == NULL || FindNextFileW(hFind, &fData) == 0)
236 return LFF_DONE;
237
238 searchRec->nameU = UNICHAR_STRDUP(fData.cFileName);
239 if (searchRec->nameU == NULL)
240 return LFF_SKIP;
241
242 searchRec->filesize = (fData.nFileSizeHigh > 0) ? -1 : fData.nFileSizeLow;
243 searchRec->isDir = (fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false;
244
245 FileTimeToSystemTime(&fData.ftLastWriteTime, &sysTime);
246 #else
247 if (hFind == NULL || (fData = readdir(hFind)) == NULL)
248 return LFF_DONE;
249
250 searchRec->nameU = UNICHAR_STRDUP(fData->d_name);
251 if (searchRec->nameU == NULL)
252 return LFF_SKIP;
253
254 searchRec->filesize = 0;
255 searchRec->isDir = (fData->d_type == DT_DIR) ? true : false;
256
257 if (fData->d_type == DT_UNKNOWN || fData->d_type == DT_LNK)
258 {
259 if (stat(fData->d_name, &st) == 0)
260 {
261 fSize = (int64_t)st.st_size;
262 searchRec->filesize = (fSize > INT32_MAX) ? -1 : (fSize & 0xFFFFFFFF);
263
264 if ((st.st_mode & S_IFMT) == S_IFDIR)
265 searchRec->isDir = true;
266 }
267 }
268 else if (!searchRec->isDir)
269 {
270 if (stat(fData->d_name, &st) == 0)
271 {
272 fSize = (int64_t)st.st_size;
273 searchRec->filesize = (fSize > INT32_MAX) ? -1 : (fSize & 0xFFFFFFFF);
274 }
275 }
276 #endif
277
278 if (searchRec->filesize < -1)
279 searchRec->filesize = -1;
280
281 if (!listEntry(searchRec))
282 {
283 // skip entry
284 free(searchRec->nameU);
285 searchRec->nameU = NULL;
286 return LFF_SKIP;
287 }
288
289 #ifdef _WIN32
290 FileTimeToSystemTime(&fData.ftLastWriteTime, &sysTime);
291 snprintf(searchRec->dateChanged, 7, "%02d%02d%02d", sysTime.wDay, sysTime.wMonth, sysTime.wYear % 100);
292 unicharToAnsi(&searchRec->firstAnsiChar, searchRec->nameU, 1);
293 #else
294 strftime(searchRec->dateChanged, 7, "%d%m%y", localtime(&st.st_mtime));
295 searchRec->firstAnsiChar = (char)searchRec->nameU[0];
296 #endif
297
298 return LFF_OK;
299 }
300
findClose(void)301 static void findClose(void)
302 {
303 if (hFind != NULL)
304 {
305 #ifdef _WIN32
306 FindClose(hFind);
307 #else
308 closedir(hFind);
309 #endif
310 hFind = NULL;
311 }
312 }
313
diskOpShowSelectText(void)314 void diskOpShowSelectText(void)
315 {
316 if (!ui.diskOpScreenShown || ui.pointerMode == POINTER_MODE_MSG1 || editor.errorMsgActive)
317 return;
318
319 if (diskop.mode == DISKOP_MODE_MOD)
320 setStatusMessage("SELECT MODULE", NO_CARRY);
321 else
322 setStatusMessage("SELECT SAMPLE", NO_CARRY);
323 }
324
handleEntryJumping(SDL_Keycode jumpToChar)325 void handleEntryJumping(SDL_Keycode jumpToChar) // SHIFT+character
326 {
327 if (diskOpEntry != NULL)
328 {
329 for (int32_t i = 0; i < diskop.numEntries; i++)
330 {
331 if (jumpToChar == diskOpEntry[i].firstAnsiChar)
332 {
333 // fix visual overrun
334 if (diskop.numEntries > DISKOP_LINES && i > diskop.numEntries-DISKOP_LINES)
335 i = diskop.numEntries - DISKOP_LINES;
336
337 diskop.scrollOffset = i;
338 ui.updateDiskOpFileList = true;
339 return;
340 }
341 }
342 }
343
344 // character not found in file list, show red mouse pointer (error)!
345 editor.errorMsgActive = true;
346 editor.errorMsgBlock = true;
347 editor.errorMsgCounter = 0;
348
349 setErrPointer();
350 }
351
diskOpEntryIsEmpty(int32_t fileIndex)352 bool diskOpEntryIsEmpty(int32_t fileIndex)
353 {
354 if (diskop.scrollOffset+fileIndex >= diskop.numEntries)
355 return true;
356
357 return false;
358 }
359
diskOpEntryIsDir(int32_t fileIndex)360 bool diskOpEntryIsDir(int32_t fileIndex)
361 {
362 if (diskOpEntry != NULL)
363 {
364 if (!diskOpEntryIsEmpty(fileIndex))
365 return diskOpEntry[diskop.scrollOffset+fileIndex].isDir;
366 }
367
368 return false; // couldn't look up entry
369 }
370
diskOpGetAnsiEntry(int32_t fileIndex)371 char *diskOpGetAnsiEntry(int32_t fileIndex)
372 {
373 UNICHAR *filenameU;
374
375 if (diskOpEntry != NULL && !diskOpEntryIsEmpty(fileIndex))
376 {
377 filenameU = diskOpEntry[diskop.scrollOffset+fileIndex].nameU;
378 if (filenameU != NULL)
379 {
380 unicharToAnsi(fileNameBuffer, filenameU, PATH_MAX);
381 return fileNameBuffer;
382 }
383 }
384
385 return NULL;
386 }
387
diskOpGetUnicodeEntry(int32_t fileIndex)388 UNICHAR *diskOpGetUnicodeEntry(int32_t fileIndex)
389 {
390 if (diskOpEntry != NULL && !diskOpEntryIsEmpty(fileIndex))
391 return diskOpEntry[diskop.scrollOffset+fileIndex].nameU;
392
393 return NULL;
394 }
395
setVisualPathToCwd(void)396 static void setVisualPathToCwd(void)
397 {
398 memset(editor.currPath, 0, PATH_MAX + 10);
399 memset(editor.currPathU, 0, (PATH_MAX + 2) * sizeof (UNICHAR));
400 UNICHAR_GETCWD(editor.currPathU, PATH_MAX);
401
402 if (diskop.mode == DISKOP_MODE_MOD)
403 UNICHAR_STRCPY(editor.modulesPathU, editor.currPathU);
404 else
405 UNICHAR_STRCPY(editor.samplesPathU, editor.currPathU);
406
407 unicharToAnsi(editor.currPath, editor.currPathU, PATH_MAX);
408 ui.updateDiskOpPathText = true;
409 }
410
changePathToHome(void)411 bool changePathToHome(void)
412 {
413 #ifdef _WIN32
414 UNICHAR pathU[PATH_MAX + 2];
415
416 if (SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, pathU) >= 0)
417 {
418 if (UNICHAR_CHDIR(pathU) == 0)
419 return true;
420 }
421
422 return false;
423 #else
424 char *homePath;
425
426 homePath = getenv("HOME");
427 if (homePath != NULL && chdir(homePath) == 0)
428 return true;
429
430 return false;
431 #endif
432 }
433
setPathFromDiskOpMode(void)434 void setPathFromDiskOpMode(void)
435 {
436 UNICHAR_CHDIR((diskop.mode == DISKOP_MODE_MOD) ? editor.modulesPathU : editor.samplesPathU);
437 setVisualPathToCwd();
438 }
439
diskOpSetPath(UNICHAR * path,bool cache)440 bool diskOpSetPath(UNICHAR *path, bool cache)
441 {
442 if (path != NULL && UNICHAR_CHDIR(path) == 0)
443 {
444 setVisualPathToCwd();
445
446 if (cache)
447 diskop.cached = false;
448
449 if (ui.diskOpScreenShown)
450 ui.updateDiskOpFileList = true;
451
452 diskop.scrollOffset = 0;
453 return true;
454 }
455 else
456 {
457 setVisualPathToCwd();
458 displayErrorMsg("CAN'T OPEN DIR !");
459 return false;
460 }
461 }
462
diskOpSetInitPath(void)463 void diskOpSetInitPath(void)
464 {
465 // default module path
466 if (config.defModulesDir[0] != '\0')
467 {
468 #ifdef _WIN32
469 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.defModulesDir, -1, pathTmp, PATH_MAX);
470 #else
471 strcpy(pathTmp, config.defModulesDir);
472 #endif
473 UNICHAR_STRCPY(editor.modulesPathU, pathTmp);
474 }
475 else
476 {
477 // no path set in config, set to current working directory
478 UNICHAR_GETCWD(editor.modulesPathU, PATH_MAX);
479 }
480
481 // default sample path
482 if (config.defSamplesDir[0] != '\0')
483 {
484 #ifdef _WIN32
485 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, config.defSamplesDir, -1, pathTmp, PATH_MAX);
486 #else
487 strcpy(pathTmp, config.defSamplesDir);
488 #endif
489 UNICHAR_STRCPY(editor.samplesPathU, pathTmp);
490 }
491 else
492 {
493 // no path set in config, set to current working directory
494 UNICHAR_GETCWD(editor.samplesPathU, PATH_MAX);
495 }
496
497 setPathFromDiskOpMode();
498 }
499
allocDiskOpVars(void)500 bool allocDiskOpVars(void)
501 {
502 editor.fileNameTmpU = (UNICHAR *)calloc(PATH_MAX + 2, sizeof (UNICHAR));
503 editor.entryNameTmp = (char *)calloc(PATH_MAX + 10, sizeof (char));
504 editor.currPath = (char *)calloc(PATH_MAX + 10, sizeof (char));
505 editor.currPathU = (UNICHAR *)calloc(PATH_MAX + 2, sizeof (UNICHAR));
506 editor.modulesPathU = (UNICHAR *)calloc(PATH_MAX + 2, sizeof (UNICHAR));
507 editor.samplesPathU = (UNICHAR *)calloc(PATH_MAX + 2, sizeof (UNICHAR));
508
509 if (editor.fileNameTmpU == NULL || editor.entryNameTmp == NULL ||
510 editor.currPath == NULL || editor.currPathU == NULL ||
511 editor.modulesPathU == NULL || editor.samplesPathU == NULL)
512 {
513 // allocated leftovers are free'd lateron
514 return false;
515 }
516
517 return true;
518 }
519
freeDiskOpMem(void)520 void freeDiskOpMem(void)
521 {
522 if (editor.fileNameTmpU != NULL) free(editor.fileNameTmpU);
523 if (editor.entryNameTmp != NULL) free(editor.entryNameTmp);
524 if (editor.currPath != NULL) free(editor.currPath);
525 if (editor.currPathU != NULL) free(editor.currPathU);
526 if (editor.modulesPathU != NULL) free(editor.modulesPathU);
527 if (editor.samplesPathU != NULL) free(editor.samplesPathU);
528 }
529
freeDiskOpEntryMem(void)530 void freeDiskOpEntryMem(void)
531 {
532 if (diskOpEntry != NULL)
533 {
534 for (int32_t i = 0; i < diskop.numEntries; i++)
535 {
536 if (diskOpEntry[i].nameU != NULL)
537 free(diskOpEntry[i].nameU);
538 }
539
540 free(diskOpEntry);
541 diskOpEntry = NULL;
542 }
543
544 diskop.numEntries = 0;
545 }
546
547 // thanks to aTc for creating this simplified routine for qsort() (I edited it a little bit)
fileEntryCompare(const void * f1,const void * f2)548 static int32_t fileEntryCompare(const void *f1, const void *f2)
549 {
550 static char fn_1[PATH_MAX+1], fn_2[PATH_MAX+1];
551
552 fileEntry_t *fe1 = (fileEntry_t *)f1;
553 fileEntry_t *fe2 = (fileEntry_t *)f2;
554
555 unicharToAnsi(fn_1, fe1->nameU, PATH_MAX);
556 unicharToAnsi(fn_2, fe2->nameU, PATH_MAX);
557
558 // ".." directories are always sorted first
559 if (fe1->isDir && strcmp(fn_1, "..") == 0) return -1;
560 if (fe2->isDir && strcmp(fn_2, "..") == 0) return 1;
561
562 /* ProTracker handles names in upper case during sorting,
563 ** convert the string case.
564 */
565 const int32_t fe1_l = (int32_t)strlen(fn_1);
566 const int32_t fe2_l = (int32_t)strlen(fn_2);
567 for (int32_t i = 0; i < fe1_l; i++) fn_1[i] = (char)toupper(fn_1[i]);
568 for (int32_t i = 0; i < fe2_l; i++) fn_2[i] = (char)toupper(fn_2[i]);
569
570 // if both entries are the same type
571 if (fe1->isDir == fe2->isDir)
572 return strcmp(fn_1, fn_2);
573
574 // different types, so one of them is a dir
575 if (fe1->isDir)
576 return -1; // first one is a dir
577
578 return 1; // second one is a dir
579 }
580
sortEntries(void)581 static void sortEntries(void)
582 {
583 if (diskop.numEntries >= 2)
584 qsort(diskOpEntry, diskop.numEntries, sizeof (fileEntry_t), fileEntryCompare);
585 }
586
diskOpFillBuffer(void)587 static bool diskOpFillBuffer(void)
588 {
589 uint8_t lastFindFileFlag;
590 fileEntry_t tmpBuffer, *newPtr;
591
592 diskop.scrollOffset = 0;
593
594 // do we have a path set?
595 if (editor.currPathU[0] == '\0')
596 setVisualPathToCwd();
597
598 freeDiskOpEntryMem();
599
600 // fill disk op. buffer (type, size, path, file name, date changed)
601
602 // read first file
603 lastFindFileFlag = findFirst(&tmpBuffer);
604 if (lastFindFileFlag != LFF_DONE && lastFindFileFlag != LFF_SKIP)
605 {
606 diskOpEntry = (fileEntry_t *)malloc(sizeof (fileEntry_t) * (diskop.numEntries + 1));
607 if (diskOpEntry == NULL)
608 {
609 findClose();
610 freeDiskOpEntryMem();
611 statusOutOfMemory();
612 return false;
613 }
614
615 memcpy(&diskOpEntry[diskop.numEntries], &tmpBuffer, sizeof (fileEntry_t));
616 diskop.numEntries++;
617 }
618
619 // read remaining files
620 while (lastFindFileFlag != LFF_DONE)
621 {
622 lastFindFileFlag = findNext(&tmpBuffer);
623 if (lastFindFileFlag != LFF_DONE && lastFindFileFlag != LFF_SKIP)
624 {
625 newPtr = (fileEntry_t *)realloc(diskOpEntry, sizeof (fileEntry_t) * (diskop.numEntries + 1));
626 if (newPtr == NULL)
627 {
628 findClose();
629 freeDiskOpEntryMem();
630 statusOutOfMemory();
631 return false;
632 }
633 diskOpEntry = newPtr;
634
635 memcpy(&diskOpEntry[diskop.numEntries], &tmpBuffer, sizeof (fileEntry_t));
636 diskop.numEntries++;
637 }
638 }
639
640 findClose();
641
642 if (diskop.numEntries > 0)
643 {
644 sortEntries();
645 }
646 else
647 {
648 // access denied or out of memory - create parent directory link
649 diskOpEntry = bufferCreateEmptyDir();
650 if (diskOpEntry != NULL)
651 diskop.numEntries = 1;
652 else
653 statusOutOfMemory();
654 }
655
656 return true;
657 }
658
diskOpFillThreadFunc(void * ptr)659 static int32_t SDLCALL diskOpFillThreadFunc(void *ptr)
660 {
661 (void)ptr;
662
663 diskop.isFilling = true;
664 diskOpFillBuffer();
665 diskop.isFilling = false;
666
667 ui.updateDiskOpFileList = true;
668 return true;
669 }
670
printFileSize(fileEntry_t * entry,uint16_t x,uint16_t y)671 static void printFileSize(fileEntry_t *entry, uint16_t x, uint16_t y)
672 {
673 char tmpStr[7];
674 uint32_t fileSize, j;
675
676 if (entry->filesize == -1) // -1 means that the original filesize is above 2GB in our directory reader
677 {
678 textOut(x, y, " >2GB", video.palette[PAL_QADSCP]);
679 return;
680 }
681
682 fileSize = (uint32_t)entry->filesize;
683 if (fileSize <= 999999)
684 {
685 // bytes
686 snprintf(tmpStr, 7, "%06d", fileSize);
687 }
688 else if (fileSize <= 9999999)
689 {
690 // kilobytes
691 fileSize /= 1000;
692 snprintf(tmpStr, 7, "%04dKB", fileSize);
693 }
694 else
695 {
696 // megabytes
697 fileSize /= 1000000;
698 snprintf(tmpStr, 7, "%04dMB", fileSize);
699 }
700
701 // turn zeroes on the left side into spaces
702 for (j = 0; j < 7; j++)
703 {
704 if (tmpStr[j] != '0')
705 break;
706
707 tmpStr[j] = ' ';
708 }
709
710 textOut(x, y, tmpStr, video.palette[PAL_QADSCP]);
711 }
712
printEntryName(char * entryName,int32_t entryLength,int32_t maxLength,uint16_t x,uint16_t y)713 static void printEntryName(char *entryName, int32_t entryLength, int32_t maxLength, uint16_t x, uint16_t y)
714 {
715 if (entryLength > maxLength)
716 {
717 // shorten name and add ".." to end
718 for (int32_t j = 0; j < maxLength-2; j++)
719 charOut(x + (j * FONT_CHAR_W), y, entryName[j], video.palette[PAL_QADSCP]);
720
721 textOut(x + ((maxLength - 2) * FONT_CHAR_W), y, "..", video.palette[PAL_QADSCP]);
722 }
723 else
724 {
725 // print whole name
726 textOut(x, y, entryName, video.palette[PAL_QADSCP]);
727 }
728 }
729
diskOpRenderFileList(void)730 void diskOpRenderFileList(void)
731 {
732 char *entryName;
733 uint8_t maxFilenameChars, maxDirNameChars;
734 uint16_t x, y, textXStart;
735 int32_t entryLength;
736 fileEntry_t *entry;
737
738 if (config.hideDiskOpDates)
739 {
740 textXStart = 8;
741 maxFilenameChars = 30;
742 maxDirNameChars = 31;
743 }
744 else
745 {
746 textXStart = 64;
747 maxFilenameChars = 23;
748 maxDirNameChars = 24;
749 }
750
751 diskOpShowSelectText();
752
753 if (diskop.forceStopReading)
754 return;
755
756 // if needed, update the file list and add entries
757 if (!diskop.cached)
758 {
759 diskop.fillThread = SDL_CreateThread(diskOpFillThreadFunc, NULL, NULL);
760 if (diskop.fillThread != NULL)
761 SDL_DetachThread(diskop.fillThread);
762
763 diskop.cached = true;
764 return;
765 }
766
767 // clear list
768 fillRect(8, 35, 295, 59, video.palette[PAL_BACKGRD]);
769
770 if (diskop.isFilling || diskOpEntry == NULL)
771 return;
772
773 // list entries
774 for (int32_t i = 0; i < DISKOP_LINES; i++)
775 {
776 if (diskop.scrollOffset+i >= diskop.numEntries)
777 break;
778
779 entry = &diskOpEntry[diskop.scrollOffset+i];
780 entryName = diskOpGetAnsiEntry(i);
781 entryLength = (int32_t)strlen(entryName);
782
783 x = textXStart;
784 y = (uint8_t)(35 + (i * (FONT_CHAR_H + 1)));
785
786 if (!entry->isDir)
787 {
788 printEntryName(entryName, entryLength, maxFilenameChars, x, y);
789
790 // print modification date
791 if (!config.hideDiskOpDates)
792 textOut(8, y, entry->dateChanged, video.palette[PAL_QADSCP]);
793
794 // print file size
795 printFileSize(entry, 256, y);
796 }
797 else
798 {
799 printEntryName(entryName, entryLength, maxDirNameChars, x, y);
800 textOut(264, y, "(DIR)", video.palette[PAL_QADSCP]);
801 }
802 }
803 }
804
diskOpLoadFile(uint32_t fileEntryRow,bool songModifiedCheck)805 void diskOpLoadFile(uint32_t fileEntryRow, bool songModifiedCheck)
806 {
807 uint8_t oldMode, oldPlayMode;
808 UNICHAR *filePath;
809
810 // if we clicked on an empty space, return...
811 if (diskOpEntryIsEmpty(fileEntryRow))
812 return;
813
814 if (diskOpEntryIsDir(fileEntryRow))
815 {
816 diskOpSetPath(diskOpGetUnicodeEntry(fileEntryRow), DISKOP_CACHE);
817 }
818 else
819 {
820 filePath = diskOpGetUnicodeEntry(fileEntryRow);
821 if (filePath != NULL)
822 {
823 if (diskop.mode == DISKOP_MODE_MOD)
824 {
825 if (songModifiedCheck && song->modified)
826 {
827 oldFileEntryRow = fileEntryRow;
828 showSongUnsavedAskBox(ASK_DISCARD_SONG);
829 return;
830 }
831
832 module_t *newSong = modLoad(filePath);
833 if (newSong != NULL)
834 {
835 oldMode = editor.currMode;
836 oldPlayMode = editor.playMode;
837
838 modStop();
839 modFree();
840
841 song = newSong;
842 setupLoadedMod();
843 song->loaded = true;
844
845 statusAllRight();
846
847 if (config.autoCloseDiskOp)
848 ui.diskOpScreenShown = false;
849
850 if (config.rememberPlayMode)
851 {
852 if (oldMode == MODE_PLAY || oldMode == MODE_RECORD)
853 {
854 editor.playMode = oldPlayMode;
855
856 if (oldPlayMode == PLAY_MODE_PATTERN || oldMode == MODE_RECORD)
857 modPlay(0, 0, 0);
858 else
859 modPlay(DONT_SET_PATTERN, 0, 0);
860
861 if (oldMode == MODE_RECORD)
862 pointerSetMode(POINTER_MODE_RECORD, DO_CARRY);
863 else
864 pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
865
866 editor.currMode = oldMode;
867 }
868 }
869 else
870 {
871 editor.currMode = MODE_IDLE;
872 editor.playMode = PLAY_MODE_NORMAL;
873 editor.songPlaying = false;
874
875 pointerSetMode(POINTER_MODE_IDLE, DO_CARRY);
876 }
877
878 displayMainScreen();
879 }
880 else
881 {
882 editor.errorMsgActive = true;
883 editor.errorMsgBlock = true;
884 editor.errorMsgCounter = 0;
885
886 // status/error message is set in the mod loader
887 setErrPointer();
888 }
889 }
890 else if (diskop.mode == DISKOP_MODE_SMP)
891 {
892 loadSample(filePath, diskOpGetAnsiEntry(fileEntryRow));
893 }
894 }
895 }
896 }
897
diskOpLoadFile2(void)898 void diskOpLoadFile2(void)
899 {
900 diskOpLoadFile(oldFileEntryRow, false);
901 }
902
renderDiskOpScreen(void)903 void renderDiskOpScreen(void)
904 {
905 blit32(0, 0, 320, 99, diskOpScreenBMP);
906
907 ui.updateDiskOpPathText = true;
908 ui.updatePackText = true;
909 ui.updateSaveFormatText = true;
910 ui.updateLoadMode = true;
911 ui.updateDiskOpFileList = true;
912 }
913
updateDiskOp(void)914 void updateDiskOp(void)
915 {
916 char tmpChar;
917
918 if (!ui.diskOpScreenShown || ui.posEdScreenShown)
919 return;
920
921 if (ui.updateDiskOpFileList)
922 {
923 ui.updateDiskOpFileList = false;
924 diskOpRenderFileList();
925 }
926
927 if (ui.updateLoadMode)
928 {
929 ui.updateLoadMode = false;
930
931 // clear boxes
932 fillRect(147, 3, FONT_CHAR_W, FONT_CHAR_H, video.palette[PAL_GENBKG]);
933 fillRect(147, 14, FONT_CHAR_W, FONT_CHAR_H, video.palette[PAL_GENBKG]);
934
935 // draw load mode arrow
936 if (diskop.mode == 0)
937 charOut(147, 3, ARROW_RIGHT, video.palette[PAL_GENTXT]);
938 else
939 charOut(147,14, ARROW_RIGHT, video.palette[PAL_GENTXT]);
940 }
941
942 if (ui.updatePackText)
943 {
944 ui.updatePackText = false;
945 textOutBg(120, 3, diskop.modPackFlg ? "ON " : "OFF", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
946 }
947
948 if (ui.updateSaveFormatText)
949 {
950 ui.updateSaveFormatText = false;
951 if (diskop.smpSaveType == DISKOP_SMP_WAV) textOutBg(120, 14, "WAV", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
952 else if (diskop.smpSaveType == DISKOP_SMP_IFF) textOutBg(120, 14, "IFF", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
953 else if (diskop.smpSaveType == DISKOP_SMP_RAW) textOutBg(120, 14, "RAW", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
954 }
955
956 if (ui.updateDiskOpPathText)
957 {
958 ui.updateDiskOpPathText = false;
959
960 // print disk op. path
961 for (int32_t i = 0; i < 26; i++)
962 {
963 tmpChar = editor.currPath[ui.diskOpPathTextOffset+i];
964 if (tmpChar == '\0')
965 tmpChar = '_';
966
967 charOutBg(24 + (i * FONT_CHAR_W), 25, tmpChar, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
968 }
969 }
970 }
971