1 /* Copyright (C) 2018
2  *
3  * Permission is hereby granted, free of charge,
4  * to any person obtaining a copy of this software and associated documentation files (the "Software"),
5  * to deal in the Software without restriction, including without limitation the rights to
6  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
7  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8  *
9  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
12  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
14  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
15  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17  */
18 
19 #include "retro_disk_control.h"
20 #include "retro_strings.h"
21 #include "retro_utils.h"
22 #include "file/file_path.h"
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #define COMMENT "#"
29 #define M3U_SPECIAL_COMMAND "#COMMAND:"
30 
31 // Return the directory name of 'filename' without trailing separator.
32 // Allocates returned string.
dirname_int(const char * filename)33 static char* dirname_int(const char* filename)
34 {
35    if (filename == NULL)
36       return NULL;
37 
38    // Find last separator
39    char* right = find_last_slash(filename);
40    if (right)
41       return strleft(filename, right - filename);
42 
43    // Not found
44    return NULL;
45 }
46 
m3u_search_file(const char * basedir,const char * dskName)47 char* m3u_search_file(const char* basedir, const char* dskName)
48 {
49    // If basedir was provided
50    if(basedir != NULL && !path_is_absolute(dskName))
51    {
52       // Join basedir and dskName
53       char* dskPath = path_join_dup(basedir, dskName);
54 
55       // Verify if this item is a relative filename (append it to the m3u path)
56       if (file_exists(dskPath))
57       {
58          // Return
59          return dskPath;
60       }
61       free(dskPath);
62    }
63 
64    // Verify if this item is an absolute pathname (or the file is in working dir)
65    if (file_exists(dskName))
66    {
67       // Copy and return
68       return strdup(dskName);
69    }
70 
71    // File not found
72    return NULL;
73 }
74 
dc_reset(dc_storage * dc)75 void dc_reset(dc_storage* dc)
76 {
77    unsigned i;
78 
79    // Verify
80    if(dc == NULL)
81       return;
82 
83    // Clean the command
84    if(dc->command)
85    {
86       free(dc->command);
87       dc->command = NULL;
88    }
89 
90    // Clean the struct
91    for(i=0; i < dc->count; i++)
92    {
93       free(dc->files[i]);
94       dc->files[i] = NULL;
95       free(dc->names[i]);
96       dc->names[i] = NULL;
97 
98       dc->types[i] = DC_IMAGE_TYPE_NONE;
99    }
100 
101    dc->unit = 0;
102    dc->count = 0;
103    dc->index = 0;
104    dc->eject_state = true;
105 }
106 
dc_create(void)107 dc_storage* dc_create(void)
108 {
109    int i;
110 
111    // Initialize the struct
112    dc_storage* dc = NULL;
113 
114    if((dc = (dc_storage*) malloc(sizeof(dc_storage))) != NULL)
115    {
116       dc->count = 0;
117       dc->index = -1;
118       dc->eject_state = true;
119       dc->command = NULL;
120       for(i = 0; i < DC_MAX_SIZE; i++)
121       {
122          dc->files[i] = NULL;
123          dc->names[i] = NULL;
124          dc->types[i] = DC_IMAGE_TYPE_NONE;
125       }
126    }
127 
128    return dc;
129 }
130 
dc_add_file_int(dc_storage * dc,char * filename,char * name)131 bool dc_add_file_int(dc_storage* dc, char* filename, char* name)
132 {
133    // Verify
134    if (filename == NULL || dc == NULL || dc->count > DC_MAX_SIZE)
135    {
136       free(filename);
137       free(name);
138 
139       return false;
140    }
141 
142    // Add the file
143    dc->count++;
144    dc->files[dc->count-1] = filename;
145    dc->names[dc->count-1]  = name;
146    dc->types[dc->count-1]  = dc_get_image_type(filename);
147 
148    printf(">>> dc added int %s - %s\n", filename, name);
149 
150    return true;
151 }
152 
dc_add_file(dc_storage * dc,const char * filename)153 bool dc_add_file(dc_storage* dc, const char* filename)
154 {
155    // Verify
156    if (dc == NULL || filename == NULL)
157         return false;
158 
159 // Determine if tape or disk fliplist from first entry
160    if (dc->unit != -1)
161    {
162       if (dc_get_image_type(dc->files[0]) == DC_IMAGE_TYPE_TAPE)
163          dc->unit = DC_IMAGE_TYPE_TAPE;
164       else if (dc_get_image_type(dc->files[0]) == DC_IMAGE_TYPE_FLOPPY)
165          dc->unit = DC_IMAGE_TYPE_FLOPPY;
166       else
167          dc->unit = DC_IMAGE_TYPE_FLOPPY;
168    }
169 
170    // Get 'name' - just the filename without extension
171    char tmp[512];
172    tmp[0] = '\0';
173    fill_short_pathname_representation(tmp, filename, sizeof(tmp));
174 
175    printf(">>> dc added ext %s - %s\n", filename, tmp);
176 
177    // Copy and return
178    return dc_add_file_int(dc, strdup(filename), strdup(tmp));
179 }
180 
dc_remove_file(dc_storage * dc,int index)181 bool dc_remove_file(dc_storage* dc, int index)
182 {
183    if (dc == NULL)
184        return false;
185 
186    if (index < 0 || index >= dc->count)
187        return false;
188 
189    // "If ptr is a null pointer, no action occurs"
190    free(dc->files[index]);
191    dc->files[index] = NULL;
192    free(dc->names[index]);
193    dc->names[index] = NULL;
194 
195    // Shift all entries after index one slot up
196    if (index != dc->count - 1)
197    {
198        memmove(dc->files + index, dc->files + index + 1, (dc->count - 1 - index) * sizeof(dc->files[0]));
199        memmove(dc->names + index, dc->names + index + 1, (dc->count - 1 - index) * sizeof(dc->names[0]));
200    }
201    dc->count--;
202 
203    // Reset fliplist unit after removing last entry
204    if (dc->count == 0)
205    {
206        dc->unit = 0;
207    }
208    return true;
209 }
210 
dc_parse_m3u(dc_storage * dc,const char * m3u_file)211 void dc_parse_m3u(dc_storage* dc, const char* m3u_file)
212 {
213    // Verify
214    if(dc == NULL)
215       return;
216 
217    if(m3u_file == NULL)
218       return;
219 
220    FILE* fp = NULL;
221 
222    // Try to open the file
223    if ((fp = fopen(m3u_file, "r")) == NULL)
224       return;
225 
226    // Reset
227    dc_reset(dc);
228 
229    // Get the m3u base dir for resolving relative path
230    char* basedir = dirname_int(m3u_file);
231 
232    // Disk control interface 'name' for the following file
233     char* image_name = NULL;
234 
235    // Read the lines while there is line to read and we have enough space
236    char buffer[2048];
237    while ((dc->count <= DC_MAX_SIZE) && (fgets(buffer, sizeof(buffer), fp) != NULL))
238    {
239       char* string = trimwhitespace(buffer);
240 
241       // If it's a m3u special key or a file
242       if (strstartswith(string, M3U_SPECIAL_COMMAND))
243       {
244          dc->command = strright(string, strlen(string) - strlen(M3U_SPECIAL_COMMAND));
245       }
246       else if (!strstartswith(string, COMMENT))
247       {
248          // Search the file (absolute, relative to m3u)
249          char* filename;
250          if ((filename = m3u_search_file(basedir, string)) != NULL)
251          {
252 
253             char tmp[512];
254             tmp[0] = '\0';
255 
256             fill_short_pathname_representation(tmp, filename, sizeof(tmp));
257             image_name = strdup(tmp);
258 
259             // Add the file to the struct
260             dc_add_file_int(dc, filename, image_name);
261             image_name = NULL;
262          }
263 
264       }
265    }
266 
267    // If basedir was provided
268    if(basedir != NULL)
269       free(basedir);
270 
271    // Close the file
272    fclose(fp);
273 
274    if (dc->count != 0)
275    {
276       if (dc_get_image_type(dc->files[0]) == DC_IMAGE_TYPE_TAPE)
277          dc->unit = DC_IMAGE_TYPE_TAPE;
278       else if (dc_get_image_type(dc->files[0]) == DC_IMAGE_TYPE_FLOPPY)
279          dc->unit = DC_IMAGE_TYPE_FLOPPY;
280       else
281          dc->unit = DC_IMAGE_TYPE_FLOPPY;
282 
283       printf(">>> dc unit: %i\n", dc->unit);
284    }
285 
286 }
287 
dc_free(dc_storage * dc)288 void dc_free(dc_storage* dc)
289 {
290    // Clean the struct
291    dc_reset(dc);
292    free(dc);
293    dc = NULL;
294    return;
295 }
296 
dc_get_image_type(const char * filename)297 enum dc_image_type dc_get_image_type(const char* filename)
298 {
299 	// Missing file
300 	if (!filename || (*filename == '\0'))
301 		return DC_IMAGE_TYPE_NONE;
302 
303 	// Floppy image
304 	if (strendswith(filename, "dsk"))
305 	   return DC_IMAGE_TYPE_FLOPPY;
306 
307 	// Tape image
308 	if (strendswith(filename, "tap") ||
309 	    strendswith(filename, "cdt"))
310 	   return DC_IMAGE_TYPE_TAPE;
311 
312 	// Fallback
313 	return DC_IMAGE_TYPE_UNKNOWN;
314 }