1 //
2 // GOATTRACKER v2 file selector
3 //
4 
5 #define GFILE_C
6 
7 #ifdef __WIN32__
8 #include <windows.h>
9 #endif
10 
11 #include "goattrk2.h"
12 
13 DIRENTRY direntry[MAX_DIRFILES];
14 
initpaths(void)15 void initpaths(void)
16 {
17   int c;
18 
19   for (c = 0; c < MAX_DIRFILES; c++)
20      direntry[c].name = NULL;
21 
22   memset(loadedsongfilename, 0, sizeof loadedsongfilename);
23   memset(songfilename, 0, sizeof songfilename);
24   memset(instrfilename, 0, sizeof instrfilename);
25   memset(songpath, 0, sizeof songpath);
26   memset(instrpath, 0, sizeof instrpath);
27   memset(packedpath, 0, sizeof packedpath);
28   strcpy(songfilter, "*.sng");
29   strcpy(instrfilter, "*.ins");
30 
31   getcwd(songpath, MAX_PATHNAME);
32   strcpy(instrpath, songpath);
33   strcpy(packedpath, songpath);
34 }
35 
fileselector(char * name,char * path,char * filter,char * title,int filemode)36 int fileselector(char *name, char *path, char *filter, char *title, int filemode)
37 {
38   int c, d, scrrep;
39   int color;
40   int files;
41   int filepos = 0;
42   int fileview = 0;
43   int lastclick = 0;
44   int lastfile = 0;
45   int lowest;
46   int exitfilesel;
47 
48   DIR *dir;
49   struct dirent *de;
50   struct stat st;
51   #ifdef __WIN32__
52   char drivestr[] = "A:\\";
53   char driveexists[26];
54   #endif
55   char cmpbuf[MAX_PATHNAME];
56   char tempname[MAX_PATHNAME];
57 
58   // Set initial path (if any)
59   if (strlen(path)) chdir(path);
60 
61   // Scan for all existing drives
62   #ifdef __WIN32__
63   for (c = 0; c < 26; c++)
64   {
65     drivestr[0] = 'A'+c;
66     if (GetDriveType(drivestr) > 1) driveexists[c] = 1;
67     else driveexists[c] = 0;
68   }
69   #endif
70 
71   // Read new directory
72   NEWPATH:
73   getcwd(path, MAX_PATHNAME);
74   files = 0;
75   // Deallocate old names
76   for (c = 0; c < MAX_DIRFILES; c++)
77   {
78     if (direntry[c].name)
79     {
80       free(direntry[c].name);
81       direntry[c].name = NULL;
82     }
83   }
84   #ifdef __WIN32__
85   // Create drive letters
86   for (c = 0; c < 26; c++)
87   {
88     if (driveexists[c])
89     {
90       drivestr[0] = 'A'+c;
91       direntry[files].name = strdup(drivestr);
92       direntry[files].attribute = 2;
93       files++;
94     }
95   }
96   #endif
97 
98   // Process directory
99   #ifdef __amigaos__
100   dir = opendir("");
101   #else
102   dir = opendir(".");
103   #endif
104   if (dir)
105   {
106     char *filtptr = strstr(filter, "*");
107     if (!filtptr) filtptr = filter;
108     else filtptr++;
109     for (c = 0; c < strlen(filter); c++)
110       filter[c] = tolower(filter[c]);
111 
112     while ((de = readdir(dir)))
113     {
114       if ((files < MAX_DIRFILES) && (strlen(de->d_name) < MAX_FILENAME))
115       {
116         direntry[files].name = strdup(de->d_name);
117         direntry[files].attribute = 0;
118         stat(de->d_name, &st);
119         if (st.st_mode & S_IFDIR)
120         {
121           direntry[files].attribute = 1;
122           files++;
123         }
124         else
125         {
126           int c;
127           // If a file, must match filter
128           strcpy(cmpbuf, de->d_name);
129           if ((!strcmp(filtptr, "*")) || (!strcmp(filtptr, ".*")))
130             files++;
131           else
132           {
133             for (c = 0; c < strlen(cmpbuf); c++)
134               cmpbuf[c] = tolower(cmpbuf[c]);
135             if (strstr(cmpbuf, filtptr))
136               files++;
137             else
138             {
139               free(direntry[files].name);
140               direntry[files].name = NULL;
141             }
142           }
143         }
144       }
145     }
146     closedir(dir);
147   }
148   // Sort the filelist in a most horrible fashion
149   for (c = 0; c < files; c++)
150   {
151     lowest = c;
152     for (d = c+1; d < files; d++)
153     {
154       if (direntry[d].attribute < direntry[lowest].attribute)
155       {
156         lowest = d;
157       }
158       else
159       {
160         if (direntry[d].attribute == direntry[lowest].attribute)
161         {
162           if (cmpname(direntry[d].name, direntry[lowest].name) < 0)
163           {
164             lowest = d;
165           }
166         }
167       }
168     }
169     if (lowest != c)
170     {
171       DIRENTRY swaptemp = direntry[c];
172       direntry[c] = direntry[lowest];
173       direntry[lowest] = swaptemp;
174     }
175   }
176 
177   // Search for the current filename
178   fileview = 0;
179   filepos = 0;
180   for (c = 0; c < files; c++)
181   {
182     if ((!direntry[c].attribute) && (!cmpname(name, direntry[c].name)))
183     {
184       filepos = c;
185     }
186   }
187 
188   exitfilesel = -1;
189   while (exitfilesel < 0)
190   {
191     int cc = cursorcolortable[cursorflash];
192     if (cursorflashdelay >= 6)
193     {
194       cursorflashdelay %= 6;
195       cursorflash++;
196       cursorflash &= 3;
197     }
198     fliptoscreen();
199     getkey();
200     if (lastclick) lastclick--;
201 
202     if (win_quitted)
203     {
204       exitprogram = 1;
205       for (c = 0; c < MAX_DIRFILES; c++)
206       {
207         if (direntry[c].name)
208         {
209           free(direntry[c].name);
210           direntry[c].name = NULL;
211         }
212       }
213       return 0;
214     }
215 
216     if (mouseb)
217     {
218       // Cancel (click outside)
219       if ((mousey < 3) || (mousey > 3+VISIBLEFILES+6) || (mousex <= 4+10) || (mousex >= 75+10))
220       {
221         if ((!prevmouseb) && (lastclick)) exitfilesel = 0;
222       }
223 
224       // Select dir,name,filter
225       if ((mousey >= 3+VISIBLEFILES+3) && (mousey <= 3+VISIBLEFILES+5) && (mousex >= 14+10) && (mousex <= 73+10))
226       {
227         filemode = mousey - (3+VISIBLEFILES+3) + 1;
228         if ((filemode == 3) && (!prevmouseb) && (lastclick)) goto ENTERFILE;
229       }
230 
231       // Select file from list
232       if ((mousey >= 3) && (mousey <= 3+VISIBLEFILES+2) && (mousex >= 6+10) && (mousex <= 73+10))
233       {
234         filemode = 0;
235         filepos = mousey - 4 - 1 + fileview;
236         if (filepos < 0) filepos = 0;
237         if (filepos > files-1) filepos = files - 1;
238 
239         if (!direntry[filepos].attribute)
240           strcpy(name, direntry[filepos].name);
241 
242         if ((!prevmouseb) && (lastclick) && (lastfile == filepos)) goto ENTERFILE;
243       }
244     }
245 
246     if (!filemode)
247     {
248       if (((key >= '0') && (key <= '0')) || ((key >= 'a') && (key <= 'z')) || ((key >= 'A') && (key <= 'Z')))
249       {
250         char k = tolower(key);
251         int oldfilepos = filepos;
252 
253         for (filepos = oldfilepos + 1; filepos < files; filepos++)
254           if (tolower(direntry[filepos].name[0]) == k) break;
255         if (filepos >= files)
256         {
257           for (filepos = 0; filepos < oldfilepos; filepos++)
258              if (tolower(direntry[filepos].name[0]) == k) break;
259         }
260 
261         if (!direntry[filepos].attribute) strcpy(name, direntry[filepos].name);
262       }
263     }
264 
265     switch(rawkey)
266     {
267       case KEY_ESC:
268       exitfilesel = 0;
269       break;
270 
271       case KEY_BACKSPACE:
272       if (!filemode)
273       {
274       #ifdef __amigaos__
275         chdir("/");
276       #else
277         chdir("..");
278       #endif
279         goto NEWPATH;
280       }
281       break;
282 
283       case KEY_HOME:
284       if (!filemode)
285       {
286         filepos = 0;
287         if (!direntry[filepos].attribute) strcpy(name, direntry[filepos].name);
288       }
289       break;
290 
291       case KEY_END:
292       if (!filemode)
293       {
294         filepos = files-1;
295         if (!direntry[filepos].attribute) strcpy(name, direntry[filepos].name);
296       }
297       break;
298 
299       case KEY_PGUP:
300       for (scrrep = PGUPDNREPEAT; scrrep; scrrep--)
301       {
302         if ((!filemode) && (filepos > 0))
303         {
304           filepos--;
305           if (!direntry[filepos].attribute) strcpy(name, direntry[filepos].name);
306         }
307       }
308       break;
309 
310       case KEY_UP:
311       if ((!filemode) && (filepos > 0))
312       {
313         filepos--;
314         if (!direntry[filepos].attribute) strcpy(name, direntry[filepos].name);
315       }
316       break;
317 
318       case KEY_PGDN:
319       for (scrrep = PGUPDNREPEAT; scrrep; scrrep--)
320       {
321         if ((!filemode) && (filepos < files-1))
322         {
323           filepos++;
324           if (!direntry[filepos].attribute) strcpy(name, direntry[filepos].name);
325         }
326       }
327       break;
328 
329       case KEY_DOWN:
330       if ((!filemode) && (filepos < files-1))
331       {
332         filepos++;
333         if (!direntry[filepos].attribute) strcpy(name, direntry[filepos].name);
334       }
335       break;
336 
337       case KEY_TAB:
338       if (!shiftpressed)
339       {
340         filemode++;
341         if (filemode > 3) filemode = 0;
342       }
343       else
344       {
345         filemode--;
346         if (filemode < 0) filemode = 3;
347       }
348       break;
349 
350       case KEY_ENTER:
351       ENTERFILE:
352       switch(filemode)
353       {
354         case 0:
355         switch (direntry[filepos].attribute)
356         {
357           case 0:
358           strcpy(name, direntry[filepos].name);
359           exitfilesel = 1;
360           break;
361 
362           case 1:
363           chdir(direntry[filepos].name);
364           goto NEWPATH;
365 
366           case 2:
367           strcpy(tempname, direntry[filepos].name);
368           if (strlen(tempname))
369           {
370             if (tempname[strlen(tempname)-1] != '\\')
371               strcat(tempname, "\\");
372           }
373           chdir(tempname);
374           goto NEWPATH;
375         }
376         break;
377 
378         case 1:
379         chdir(path);
380         case 2:
381         filemode = 0;
382         goto NEWPATH;
383 
384         case 3:
385         exitfilesel = 1;
386         break;
387       }
388       break;
389     }
390 
391     switch(filemode)
392     {
393       case 1:
394       editstring(path, MAX_PATHNAME);
395       break;
396 
397       case 2:
398       editstring(filter, MAX_FILENAME);
399       break;
400 
401       case 3:
402       editstring(name, MAX_FILENAME);
403       break;
404     }
405 
406     // Validate filelist view
407     if (filepos < fileview) fileview = filepos;
408     if (filepos - fileview >= VISIBLEFILES) fileview = filepos - VISIBLEFILES + 1;
409 
410     // Refresh fileselector display
411     if (isplaying()) printstatus();
412     for (c = 0; c < VISIBLEFILES+7; c++)
413     {
414       printblank(50-(MAX_FILENAME+10)/2, 3+c, MAX_FILENAME+10);
415     }
416     drawbox(50-(MAX_FILENAME+10)/2, 3, 15, MAX_FILENAME+10, VISIBLEFILES+7);
417     printblankc(50-(MAX_FILENAME+10)/2+1, 4, 15+16,MAX_FILENAME+8);
418     printtext(50-(MAX_FILENAME+10)/2+1, 4, 15+16, title);
419 
420     for (c = 0; c < VISIBLEFILES; c++)
421     {
422       if ((fileview+c >= 0) && (fileview+c < files))
423       {
424         switch (direntry[fileview+c].attribute)
425         {
426           case 0:
427           sprintf(textbuffer, "%-60s        ", direntry[fileview+c].name);
428           break;
429 
430           case 1:
431           sprintf(textbuffer, "%-60s   <DIR>", direntry[fileview+c].name);
432           break;
433 
434           case 2:
435           sprintf(textbuffer, "%-60s   <DRV>", direntry[fileview+c].name);
436           break;
437         }
438       }
439       else
440       {
441         sprintf(textbuffer, "                                                                    ");
442       }
443       color = CNORMAL;
444       if ((fileview+c) == filepos) color = CEDIT;
445       textbuffer[68] = 0;
446       printtext(50-(MAX_FILENAME+10)/2+1, 5+c, color, textbuffer);
447       if ((!filemode) && ((fileview+c) == filepos)) printbg(50-(MAX_FILENAME+10)/2+1, 5+c, cc, 68);
448     }
449 
450     printtext(50-(MAX_FILENAME+10)/2+1, 6+VISIBLEFILES, 15, "PATH:   ");
451     sprintf(textbuffer, "%-60s", path);
452     textbuffer[MAX_FILENAME] = 0;
453     color = CNORMAL;
454     if (filemode == 1) color = CEDIT;
455     printtext(50-(MAX_FILENAME+10)/2+9, 6+VISIBLEFILES, color, textbuffer);
456     if ((filemode == 1) && (strlen(path) < MAX_FILENAME)) printbg(50-(MAX_FILENAME+10)/2+9+strlen(path), 6+VISIBLEFILES, cc, 1);
457 
458     printtext(50-(MAX_FILENAME+10)/2+1, 7+VISIBLEFILES, 15, "FILTER: ");
459     sprintf(textbuffer, "%-60s", filter);
460     textbuffer[MAX_FILENAME] = 0;
461     color = CNORMAL;
462     if (filemode == 2) color = CEDIT;
463     printtext(50-(MAX_FILENAME+10)/2+9, 7+VISIBLEFILES, color, textbuffer);
464     if (filemode == 2) printbg(50-(MAX_FILENAME+10)/2+9+strlen(filter), 7+VISIBLEFILES, cc, 1);
465 
466     printtext(50-(MAX_FILENAME+10)/2+1, 8+VISIBLEFILES, 15, "NAME:   ");
467     sprintf(textbuffer, "%-60s", name);
468     textbuffer[MAX_FILENAME] = 0;
469     color = CNORMAL;
470     if (filemode == 3) color = CEDIT;
471     printtext(50-(MAX_FILENAME+10)/2+9, 8+VISIBLEFILES, color, textbuffer);
472     if (filemode == 3) printbg(50-(MAX_FILENAME+10)/2+9+strlen(name), 8+VISIBLEFILES, cc, 1);
473 
474     if (win_quitted) exitfilesel = 0;
475 
476     if ((mouseb) && (!prevmouseb))
477     {
478       lastclick = DOUBLECLICKDELAY;
479       lastfile = filepos;
480     }
481   }
482 
483   // Deallocate all used names
484   for (c = 0; c < MAX_DIRFILES; c++)
485   {
486     if (direntry[c].name)
487     {
488       free(direntry[c].name);
489       direntry[c].name = NULL;
490     }
491   }
492 
493   // Restore screen & exit
494   printmainscreen();
495   return exitfilesel;
496 }
497 
editstring(char * buffer,int maxlength)498 void editstring(char *buffer, int maxlength)
499 {
500   int len = strlen(buffer);
501 
502   if (key)
503   {
504     if ((key >= 32) && (key < 256))
505     {
506       if (len < maxlength-1)
507       {
508         buffer[len] = key;
509         buffer[len+1] = 0;
510       }
511     }
512     if ((key == 8) && (len > 0))
513     {
514       buffer[len-1] = 0;
515     }
516   }
517 }
518 
cmpname(char * string1,char * string2)519 int cmpname(char *string1, char *string2)
520 {
521   for (;;)
522   {
523     unsigned char char1 = tolower(*string1++);
524     unsigned char char2 = tolower(*string2++);
525     if (char1 < char2) return -1;
526     if (char1 > char2) return 1;
527     if ((!char1) || (!char2)) return 0;
528   }
529 }
530 
531