1 //
2 // A dirty sidtune format converter hack with almost no security code.
3 // Copyright (C) Michael Schwendt.
4 //
5 //  This program is free software; you can redistribute it and/or modify
6 //  it under the terms of the GNU General Public License as published by
7 //  the Free Software Foundation; either version 2 of the License, or
8 //  (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 //
19 
20 #if defined(HAVE_FREEBSD)
21 #include <sys/types.h>
22 #endif
23 #include <ctype.h>
24 #include <dirent.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <iostream>
29 #include <iomanip>
30 
31 #include <sidplay/sidtune.h>
32 #include <sidplay/fformat.h>
33 
34 using std::cerr;
35 using std::cin;
36 using std::cout;
37 using std::endl;
38 using std::flush;
39 
40 static bool toPSID = true,
41     toSIDPLAY = false,
42     checkOnly = false,
43     processDir = false,
44     recursive = false,
45     assumeAll = false;
46 
47 static bool isDir(const char* file);
48 static void recurseDir(DIR* directory, const char* path);
49 static void handleFile(const char* file);
50 static void printUsage();
51 
main(int argc,char * argv[])52 int main(int argc, char* argv[])
53 {
54     DIR *directory;
55     if ((directory = opendir(".")) == 0)
56     {
57         cerr << "ERROR: Cannot read current directory." << endl;
58         exit(-1);
59     }
60 
61     if (argc < 2)
62     {
63         printUsage();
64         exit(-1);
65     }
66 
67     int a = 0;
68 
69     while (a++ < argc-1)
70     {
71         if (myStrNcaseCmp(argv[a],"--psid") == 0)
72         {
73             toPSID = true;
74             toSIDPLAY = false;
75         }
76         else if (myStrNcaseCmp(argv[a],"--sidplay") == 0)
77         {
78             toPSID = false;
79             toSIDPLAY = true;
80         }
81         else if (myStrNcaseCmp(argv[a],"--") == 0)
82         {
83             printUsage();
84             exit(-1);
85         }
86         else if (myStrNcaseCmp(argv[a],"-a") == 0)
87         {
88             assumeAll = true;
89         }
90         else if (myStrNcaseCmp(argv[a],"-c") == 0)
91         {
92             checkOnly = true;
93         }
94         else if (myStrNcaseCmp(argv[a],"-d") == 0)
95         {
96             processDir = true;
97         }
98         else if (myStrNcaseCmp(argv[a],"-r") == 0)
99         {
100             recursive = true;
101         }
102         else if (myStrNcaseCmp(argv[a],"-") == 0)
103         {
104             printUsage();
105             exit(-1);
106         }
107     }
108 
109     if (recursive)
110     {
111 		recurseDir(directory,".");
112         exit(0);
113     }
114 
115     if (processDir)
116     {
117         dirent *entry;
118         while ((entry = readdir(directory)) != 0)
119         {
120             if ((strcmp(entry->d_name,".")!=0) &&
121                 (strcmp(entry->d_name,"..")!=0) &&
122 				!isDir(entry->d_name))
123             {
124                 handleFile(entry->d_name);
125             }
126         }
127         closedir(directory);
128         exit(0);
129     }
130 
131     // Check for any filenames given.
132     bool success = true;
133     a = 0;
134     while (a++ < argc-1)
135     {
136         success = true;  // reset flag for each new file
137 
138         if (myStrNcaseCmp(argv[a],"-") != 0)
139         {
140             handleFile(argv[a]);
141         }
142     }
143 }
144 
printUsage()145 static void printUsage()
146 {
147     cerr
148         << "Usage: sidcon <options> [--psid|--sidplay] <files>" << endl
149         << "options: -a    assume yes (y) on all questions" << endl
150         << "         -c    only check each file's loadaddress" << endl
151         << "         -d    process current directory" << endl
152         << "         -r    recurse into current directory tree" << endl;
153 }
154 
handleFile(const char * file)155 static void handleFile(const char* file)
156 {
157     cout << file << ' ';
158 
159     char* outFileName = 0;
160     sidTune mySidTune(file,true,0);  // true => compile and run on MS Windows
161     sidTuneInfo mySTI;
162 
163     bool success = mySidTune.getStatus();
164     bool saveIt = !checkOnly;
165 
166 	if (!success)
167 	{
168 		mySidTune.getInfo(mySTI);
169 		cerr << mySTI.statusString << endl;
170 	}
171 
172 	char* origDataName = 0;
173 	char* infoFileName = 0;
174 
175     if (success)
176     {
177         cout << '.';
178         success &= mySidTune.getInfo(mySTI);
179         if (mySTI.fixLoad)
180         {
181             cout << endl << "Duplicate C64 load address detected. Fix it? (y/n) " << flush;
182             bool fixIt = false;
183             if (assumeAll)
184             {
185                 cout << endl;
186                 fixIt = true;
187             }
188             else
189             {
190                 char c;
191                 cin >> c;
192                 fixIt = (tolower(c)=='y');
193             }
194             if (fixIt)
195             {
196                 mySidTune.fixLoadAddress();
197                 saveIt = true;
198             }
199         }
200         success &= (mySTI.dataFileName!=0);
201         if (success)
202         {
203             success &= (0!=(outFileName = new char[strlen(mySTI.path)+strlen(mySTI.dataFileName)+4+1]));
204             if (success)
205             {
206                 strcpy(outFileName,mySTI.path);
207                 strcat(outFileName,mySTI.dataFileName);
208 				origDataName = myStrDup(outFileName);  // save it for error recovery
209             }
210         }
211     }
212 
213  	char* tmp = fileNameWithoutPath(tmpnam(NULL));
214     char* tmpDataName = new char[strlen(mySTI.path)+strlen(tmp)+1];
215     strcpy(tmpDataName,mySTI.path);
216     strcat(tmpDataName,tmp);
217 
218  	tmp = fileNameWithoutPath(tmpnam(NULL));
219     char* tmpInfoName = new char[strlen(mySTI.path)+strlen(tmp)+1];
220     strcpy(tmpInfoName,mySTI.path);
221     strcat(tmpInfoName,tmp);
222 
223     if (success && saveIt)
224     {
225         cout << '.';
226 		success &= (0==rename(outFileName,tmpDataName));  // outFileName = path+dataFileName
227         if (mySTI.infoFileName != 0)
228         {
229             strcpy(outFileName,mySTI.path);
230             strcat(outFileName,mySTI.infoFileName);
231 			infoFileName = myStrDup(outFileName);  // save it for error recovery
232 			success &= (0==rename(outFileName,tmpInfoName));
233         }
234 		else
235 		{
236 			tmpInfoName = 0;
237 		}
238     }
239 
240 
241     if (success && saveIt)
242     {
243         cout << '.';
244         if (toPSID)
245         {
246             strcpy(fileExtOfPath(outFileName),".sid");
247             success &= (mySidTune.savePSIDfile(outFileName));
248         }
249         else if (toSIDPLAY)
250         {
251             strcpy(fileExtOfPath(outFileName),".c64");
252             success &= (mySidTune.saveC64dataFile(outFileName));
253             strcpy(fileExtOfPath(outFileName),".sid");
254             success &= (mySidTune.saveSIDfile(outFileName));
255         }
256 
257 		if (success)
258 		{
259 			remove(tmpDataName);
260 			if (tmpInfoName != 0)
261 				remove(tmpInfoName);
262 		}
263 		else  // recover tmp files
264 		{
265 			rename(tmpDataName,origDataName);
266 			if (tmpInfoName != 0)
267 				rename(tmpInfoName,infoFileName);
268 		}
269     }
270 
271     if (success && saveIt)
272     {
273         cout << " SAVED" << endl;
274     }
275     else if (success)
276     {
277         cout << " OK" << endl;
278     }
279 
280     if (tmpDataName != 0)
281         delete[] tmpDataName;
282     if (tmpInfoName != 0)
283         delete[] tmpInfoName;
284     if (outFileName != 0)
285         delete[] outFileName;
286 	if (origDataName != 0)
287 		delete[] origDataName;
288 	if (infoFileName != 0)
289 		delete[] infoFileName;
290 }
291 
isDir(const char * file)292 bool isDir(const char* file)
293 {
294 	DIR *directory;
295 	if ((directory = opendir(file)) == 0)
296 		return false;
297 	else
298 	{
299 		closedir(directory);
300 		return true;
301 	}
302 }
303 
recurseDir(DIR * directory,const char * path)304 void recurseDir(DIR* directory, const char* path)
305 {
306 	dirent *entry;
307 	while ((entry = readdir(directory)) != 0)
308 	{
309 		char* file = new char[strlen(path)+strlen(entry->d_name)+1+1];
310 		strcpy(file,path);
311 		strcat(file,"/");
312 		strcat(file,entry->d_name);
313 		if ((strcmp(entry->d_name,".")!=0) &&
314 			(strcmp(entry->d_name,"..")!=0) &&
315 			!isDir(file))
316 		{
317 			handleFile(file);
318 		}
319 		else if ((strcmp(entry->d_name,".")!=0) &&
320 				 (strcmp(entry->d_name,"..")!=0) &&
321 				 isDir(file))
322 		{
323 			char* newPath = new char[strlen(path)+strlen(entry->d_name)+1+1];
324 			strcpy(newPath,path);
325 			strcat(newPath,"/");
326 			strcat(newPath,entry->d_name);
327 			DIR* nextDir;
328 			if ((nextDir = opendir(newPath)) == 0)
329 			{
330 				cerr << "ERROR: Cannot read directory ``" << newPath << "''." << endl;
331 			}
332 			else
333 			{
334 				recurseDir(nextDir,newPath);
335 			}
336 			delete[] newPath;
337 		}
338 		delete[] file;
339 	}
340 	closedir(directory);
341 }
342