1 //
2 // /home/ms/source/sidplay/libsidplay/RCS/sidtune.cpp,v
3 //
4 // Information on usage of this class in "include/sidtune.h".
5 //
6 
7 #include "compconf.h"
8 #ifdef SID_HAVE_EXCEPTIONS
9 #include <new>
10 #endif
11 #include <iostream>
12 #include <iomanip>
13 #include <fstream>
14 using namespace std;
15 #include <cstring>
16 #include <climits>
17 
18 #undef SID_USE_STAT
19 #ifdef SID_HAVE_UNISTD_H
20 #ifdef SID_HAVE_SYS_TYPES_H
21 #ifdef SID_HAVE_SYS_STAT_H
22 #define SID_USE_STAT
23 # include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #endif
27 #endif
28 #endif
29 #ifndef SID_USE_STAT
30 #include <stdio.h>
31 #endif
32 
33 #include "sidtune.h"
34 #include "fformat.h"
35 #include "myendian.h"
36 #include "pp.h"
37 
38 using namespace std;
39 
40 
41 const char text_songNumberExceed[] = "WARNING: Selected song number was too high";
42 const char text_emptyFile[] = "ERROR: File is empty";
43 const char text_unrecognizedFormat[] = "ERROR: Could not determine file format";
44 const char text_noDataFile[] = "ERROR: Did not find the corresponding data file";
45 const char text_notEnoughMemory[] = "ERROR: Not enough free memory";
46 const char text_cantLoadFile[] = "ERROR: Could not load input file";
47 const char text_cantOpenFile[] = "ERROR: Could not open file for binary input";
48 const char text_fileTooLong[] = "ERROR: Input data too long";
49 const char text_dataTooLong[] = "ERROR: Music data size exceeds C64 memory";
50 const char text_cantCreateFile[] = "ERROR: Could not create output file";
51 const char text_fileIoError[] = "ERROR: File I/O error";
52 const char text_fatalInternal[] = "FATAL: Internal error - contact the developers";
53 const char text_VBI[] = "VBI";
54 const char text_CIA[] = "CIA 1 Timer A";
55 const char text_PAL_VBI[] = "50 Hz VBI (PAL)";
56 const char text_PAL_CIA[] = "CIA 1 Timer A (PAL)";
57 const char text_NTSC_VBI[] = "60 Hz VBI (NTSC)";
58 const char text_NTSC_CIA[] = "CIA 1 Timer A (NTSC)";
59 const char text_noErrors[] = "No errors";
60 const char text_na[] = "N/A";
61 
62 // Default sidtune file name extensions. This selection can be overriden
63 // by specifying a custom list in the constructor.
64 const char *defaultFileNameExt[] =
65 {
66 	// Preferred default file extension for single-file sidtunes
67 	// or sidtune description files in SIDPLAY INFOFILE format.
68 	".sid",
69 	// Common file extension for single-file sidtunes due to SIDPLAY/DOS
70 	// displaying files *.DAT in its file selector by default.
71 	// Originally this was intended to be the extension of the raw data file
72 	// of two-file sidtunes in SIDPLAY INFOFILE format.
73 	".dat",
74 	// Extension of Amiga Workbench tooltype icon info files, which
75 	// have been cut to MS-DOS file name length (8.3).
76 	".inf",
77 	// No extension for the raw data file of two-file sidtunes in
78 	// PlaySID Amiga Workbench tooltype icon info format.
79 	"",
80 	// Common upper-case file extensions from MS-DOS (unconverted).
81 	".DAT", ".SID", ".INF",
82 	// File extensions used (and created) by various C64 emulators and
83 	// related utilities. These extensions are recommended to be used as
84 	// a replacement for ".dat" in conjunction with two-file sidtunes.
85 	".c64", ".prg", ".C64", ".PRG",
86 	// Uncut extensions from Amiga.
87 	".info", ".INFO", ".data", ".DATA",
88 	// End.
89 	0
90 };
91 
92 // ------------------------------------------------- constructors, destructor
93 
sidTune(const char * fileName,const char ** fileNameExt)94 sidTune::sidTune( const char* fileName, const char **fileNameExt )
95 {
96 	safeConstructor();
97 	isSlashedFileName = false;
98 	setFileNameExtensions(fileNameExt);
99 	if (fileName != 0)
100 	{
101 #if !defined(SID_NO_STDIN_LOADER)
102 		// Filename ``-'' is used as a synonym for standard input.
103 		if ( strcmp( fileName, "-" ) == 0 )
104 		{
105 			stdinConstructor();
106 		}
107 		else
108 		{
109 #endif
110 			filesConstructor( fileName );
111 #if !defined(SID_NO_STDIN_LOADER)
112 		}
113 #endif
114 		deleteFileBuffers();
115 	}
116 }
117 
sidTune(const char * fileName,const bool separatorIsSlash,const char ** fileNameExt)118 sidTune::sidTune(const char* fileName, const bool separatorIsSlash,
119                  const char **fileNameExt)
120 {
121 	safeConstructor();
122 	isSlashedFileName = separatorIsSlash;
123 	setFileNameExtensions(fileNameExt);
124 	if (fileName != 0)
125 	{
126 #if !defined(SID_NO_STDIN_LOADER)
127 		// Filename ``-'' is used as a synonym for standard input.
128 		if ( strcmp( fileName, "-" ) == 0 )
129 		{
130 			stdinConstructor();
131 		}
132 		else
133 		{
134 #endif
135 			filesConstructor( fileName );
136 			deleteFileBuffers();
137 #if !defined(SID_NO_STDIN_LOADER)
138 		}
139 #endif
140 	}
141 }
142 
sidTune(const ubyte * data,udword dataLen)143 sidTune::sidTune(const ubyte* data, udword dataLen)
144 {
145 	safeConstructor();
146 	bufferConstructor(data,dataLen);
147 }
148 
149 
~sidTune()150 sidTune::~sidTune()
151 {
152 	safeDestructor();
153 }
154 
155 
156 // -------------------------------------------------- public member functions
157 
setFileNameExtensions(const char ** fileNameExt)158 void sidTune::setFileNameExtensions(const char **fileNameExt)
159 {
160 	if (fileNameExt != 0)
161 		fileNameExtensions = fileNameExt;
162 	else
163 		fileNameExtensions = defaultFileNameExt;
164 }
165 
load(const ubyte * data,udword dataLen)166 bool sidTune::load(const ubyte* data, udword dataLen)
167 {
168 	safeDestructor();
169 	safeConstructor();
170 	bufferConstructor(data,dataLen);
171 	return status;
172 }
173 
open(const char * fileName)174 bool sidTune::open(const char* fileName)
175 {
176 	safeDestructor();
177 	safeConstructor();
178     isSlashedFileName = false;
179 	filesConstructor(fileName);
180 	deleteFileBuffers();
181 	return status;
182 }
183 
open(const char * fileName,const bool separatorIsSlash)184 bool sidTune::open(const char* fileName, const bool separatorIsSlash)
185 {
186 	safeDestructor();
187 	safeConstructor();
188 	isSlashedFileName = separatorIsSlash;
189 	filesConstructor(fileName);
190 	deleteFileBuffers();
191 	return status;
192 }
193 
setInfo(sidTuneInfo & inInfo)194 bool sidTune::setInfo( sidTuneInfo & inInfo )
195 {
196     // dummy
197     return true;
198 }
199 
getInfo(sidTuneInfo & outInfo)200 bool sidTune::getInfo( sidTuneInfo& outInfo )
201 {
202 	outInfo = info;
203 	return true;
204 }
205 
206 // First check, whether a song is valid. Then copy any song-specific
207 // variable information such a speed/clock setting to the info structure.
208 //
209 // This is a private member function. It is used only by player.cpp.
selectSong(uword selectedSong)210 uword sidTune::selectSong(uword selectedSong)
211 {
212 	// Determine and set starting song number.
213 	if (selectedSong == 0)
214 	{
215 		selectedSong = info.startSong;
216 	}
217 	else if ((selectedSong > info.songs) || (selectedSong > classMaxSongs))
218 	{
219 		info.statusString = text_songNumberExceed;
220 		selectedSong = info.startSong;
221 	}
222 	info.lengthInSeconds = songLength[selectedSong-1];
223 	// Retrieve song speed definition.
224 	info.songSpeed = songSpeed[selectedSong-1];
225     // NB! Prior to song initialization, the following is the same
226     // than info.clock.
227 	info.clockSpeed = clockSpeed[selectedSong-1];
228 	// Assign song speed description string depending on clock speed.
229     // Final speed description is available only after song init.
230     if (info.songSpeed == SIDTUNE_SPEED_VBI)
231     {
232         info.speedString = text_VBI;
233     }
234     else
235     {
236         info.speedString = text_CIA;
237     }
238 	return (info.currentSong=selectedSong);
239 }
240 
fixLoadAddress(bool force,uword init,uword play)241 void sidTune::fixLoadAddress(bool force, uword init, uword play)
242 {
243     if (info.fixLoad || force)
244     {
245         info.fixLoad = false;
246         info.loadAddr += 2;
247         fileOffset += 2;
248 
249         if (force)
250         {
251             info.initAddr = init;
252             info.playAddr = play;
253         }
254     }
255 }
256 
257 // --------------------------------------------------------------------------
258 
setIRQaddress(uword address)259 void sidTune::setIRQaddress(uword address)
260 {
261 	info.irqAddr = address;
262 }
263 
placeSidTuneInC64mem(ubyte * c64buf)264 bool sidTune::placeSidTuneInC64mem( ubyte* c64buf )
265 {
266 	if (isCached && status)
267 	{
268 		// Check the size of the data.
269 		if ( info.c64dataLen > 65536 )
270 		{
271 			info.statusString = text_dataTooLong;
272 			return (status = false);
273 		}
274 		else
275 		{
276 			udword endPos = info.loadAddr + info.c64dataLen;
277 			if (endPos <= 65536)
278 			{
279 				// Copy data from cache to the correct destination.
280 				memcpy(c64buf+info.loadAddr,cachePtr+fileOffset,info.c64dataLen);
281 			}
282 			else
283 			{
284 				// Security - split data which would exceed the end of the C64 memory.
285 				// Memcpy could not detect this.
286 				memcpy(c64buf+info.loadAddr,cachePtr+fileOffset,info.c64dataLen-(endPos-65536));
287 				// Wrap the remaining data to the start address of the C64 memory.
288 				memcpy(c64buf,cachePtr+fileOffset+info.c64dataLen-(endPos-65536),(endPos-65536));
289 			}
290 			return (status = true);
291 		}
292 	}
293 	else
294 	{
295 		return (status = false);
296 	}
297 }
298 
299 
loadFile(const char * fileName,ubyte ** bufferRef)300 udword sidTune::loadFile(const char* fileName, ubyte** bufferRef)
301 {
302 	udword fileLen = 0;
303 	status = false;
304     // Work-around for bugs in Standard library.
305     if ( !fileExists( fileName )  )
306     {
307 		info.statusString = text_cantOpenFile;
308         return 0;
309     }
310     // Open binary input file stream at end of file.
311 #if defined(SID_HAVE_IOS_BIN)
312 	ifstream myIn( fileName, ios::in|ios::bin );
313 #else
314 	ifstream myIn( fileName, ios::in|ios::binary );
315 #endif
316 	// As a replacement for !is_open(), bad() and the NOT-operator
317     // don't seem to work on all systems.
318 #if defined(SID_DONT_HAVE_IS_OPEN)
319     if ( !myIn )
320 #else
321 	if ( !myIn.is_open() )
322 #endif
323 	{
324 		info.statusString = text_cantOpenFile;
325         return 0;
326 	}
327     // Check for PowerPacker compression: load and decompress, if PP20 file.
328     if (depp(myIn,bufferRef))
329     {
330         // Decompression successful, use uncompressed datafilelen.
331         fileLen = ppUncompressedLen();
332         info.statusString = ppErrorString;
333         status = true;
334         return fileLen;
335     }
336     else if (ppIsCompressed())
337     {
338         // An error occured while decompressing.
339         info.statusString = ppErrorString;
340         return 0;
341     }
342     // Should be uncompressed file.
343 #if defined(SID_HAVE_SEEKG_OFFSET)
344     fileLen = (myIn.seekg(0,ios::end)).offset();
345 #else
346     myIn.seekg(0, ios::end);
347     fileLen = (udword)myIn.tellg();
348 #endif
349     if ( *bufferRef != 0 )
350     {
351         delete[] *bufferRef;  // free previously allocated memory
352     }
353 #ifdef SID_HAVE_EXCEPTIONS
354     *bufferRef = new(std::nothrow) ubyte[fileLen+1];
355 #else
356     *bufferRef = new ubyte[fileLen+1];
357 #endif
358     if ( *bufferRef == 0 )
359     {
360         info.statusString = text_notEnoughMemory;
361         fileLen = 0;  // returning 0 = error condition.
362     }
363     else
364     {
365         *(*bufferRef+fileLen) = 0;
366     }
367     // Load uncompressed file.
368     myIn.seekg(0, ios::beg);
369     udword restFileLen = fileLen;
370     while ( restFileLen > INT_MAX )
371     {
372         myIn.read( (char*)*bufferRef + (fileLen - restFileLen), INT_MAX );
373         restFileLen -= INT_MAX;
374     }
375     if ( restFileLen > 0 )
376     {
377         myIn.read( (char*)*bufferRef + (fileLen - restFileLen), restFileLen );
378     }
379     if ( myIn.bad() )
380     {
381         info.statusString = text_cantLoadFile;
382     }
383     else
384     {
385         info.statusString = text_noErrors;
386         status = true;
387     }
388     myIn.close();
389     if (fileLen == 0)
390     {
391         info.statusString = text_emptyFile;
392         status = false;
393     }
394 	return fileLen;
395 }
396 
397 
398 // protected
fileExists(const char * fileName)399 bool sidTune::fileExists( const char* fileName )
400 {
401 #ifdef SID_USE_STAT
402     struct stat fileStat;
403     return ( stat( fileName, &fileStat ) == 0  &&
404          S_ISREG( fileStat.st_mode )  );
405 #else
406     FILE* f = fopen( fileName, "r+b" );
407     bool exists = (f!=NULL);
408     if ( exists )
409         fclose( f );
410     return exists;
411 #endif
412 }
413 
414 
deleteFileBuffers()415 void sidTune::deleteFileBuffers()
416 {
417 	// This function does not affect status and statusstring.
418 	// The filebuffers are global to the object.
419 	if ( fileBuf != 0 )
420 	{
421 		delete[] fileBuf;
422 		fileBuf = 0;
423 	}
424 	if ( fileBuf2 != 0 )
425 	{
426 		delete[] fileBuf2;
427 		fileBuf2 = 0;
428 	}
429 }
430 
deleteFileNameCopies()431 void sidTune::deleteFileNameCopies()
432 {
433 	// When will it be fully safe to call delete[] (0)?
434 	if ( info.dataFileName != 0 )
435 		delete[] info.dataFileName;
436 	if ( info.infoFileName != 0 )
437 		delete[] info.infoFileName;
438 	if ( info.path != 0 )
439 		delete[] info.path;
440 	info.dataFileName = 0;
441 	info.infoFileName = 0;
442 	info.path = 0;
443 }
444 
445 
cacheRawData(const void * sourceBuf,udword sourceBufLen)446 bool sidTune::cacheRawData(const void* sourceBuf, udword sourceBufLen)
447 {
448 	clearCache();
449 #ifdef SID_HAVE_EXCEPTIONS
450 	if ( (cachePtr = new(std::nothrow) ubyte[sourceBufLen]) == 0 )
451 #else
452 	if ( (cachePtr = new ubyte[sourceBufLen]) == 0 )
453 #endif
454 	{
455 		info.statusString = text_notEnoughMemory;
456 		return (status = false);
457 	}
458 	else
459         {
460             if (sourceBufLen >= 2)
461             {
462                 // We only detect an offset of two. Some position independent
463                 // sidtunes contain a load address of 0xE000, but are loaded
464                 // to 0x0FFE and call player at 0x1000.
465                 info.fixLoad = (readLEword((const ubyte*)sourceBuf+fileOffset)==(info.loadAddr+2));
466             }
467 		memcpy(cachePtr, (const ubyte*)sourceBuf, sourceBufLen);
468 		cacheLen = sourceBufLen;
469 		isCached = true;
470 		info.statusString = text_noErrors;
471 		return (status = true);
472 	}
473 }
474 
clearCache()475 void sidTune::clearCache()
476 {
477 	if ( cachePtr != 0 )
478 	{
479 		delete[] cachePtr;
480 		cachePtr = 0;
481 	}
482 	cacheLen = 0;
483 	isCached = false;
484 }
485 
getCachedRawData(void * destBuf,udword destBufLen)486 bool sidTune::getCachedRawData( void* destBuf, udword destBufLen )
487 {
488 	if (( cachePtr == 0 ) || ( cacheLen > destBufLen ))
489 	{
490 		info.statusString = text_fatalInternal;
491 		return (status = false);
492 	}
493 	memcpy( (ubyte*)destBuf, cachePtr, cacheLen );
494 	info.dataFileLen = cacheLen;
495 	info.statusString = text_noErrors;
496 	return (status = true);
497 }
498 
499 
safeConstructor()500 void sidTune::safeConstructor()
501 {
502 	// Initialize the object with some safe defaults.
503 	status = false;
504 
505 	info.statusString = text_na;
506 	info.path = info.infoFileName = info.dataFileName = 0;
507 	info.dataFileLen = info.c64dataLen = 0;
508 	info.formatString = text_na;
509 	info.speedString = text_na;
510 	info.loadAddr = ( info.initAddr = ( info.playAddr = 0 ));
511 	info.songs = ( info.startSong = ( info.currentSong = 0 ));
512 	info.musPlayer = false;
513 	info.psidSpecific = false;
514 	info.fixLoad = false;
515     info.clock = SIDTUNE_CLOCK_UNKNOWN;
516     info.sidModel = SIDTUNE_SIDMODEL_UNKNOWN;
517 	info.songSpeed = SIDTUNE_SPEED_VBI;
518 	info.clockSpeed = SIDTUNE_CLOCK_UNKNOWN;
519 	info.lengthInSeconds = 0;
520     info.relocStartPage = 0;
521     info.relocPages = 0;
522     info.reserved = 0;
523 
524 	for ( uint si = 0; si < classMaxSongs; si++ )
525 	{
526 		songSpeed[si] = SIDTUNE_SPEED_VBI;
527 		clockSpeed[si] = SIDTUNE_CLOCK_UNKNOWN;
528 		songLength[si] = 0;
529 	}
530 
531 	cachePtr = 0;
532 	cacheLen = 0;
533 
534     isSlashedFileName = false;
535 
536 	fileBuf = ( fileBuf2 = 0 );
537 	fileOffset = 0;
538 	fileNameExtensions = defaultFileNameExt;
539 
540 	for ( uint sNum = 0; sNum < infoStringNum; sNum++ )
541 	{
542 		for ( uint sPos = 0; sPos < infoStringLen; sPos++ )
543 		{
544 			infoString[sNum][sPos] = 0;
545 		}
546 	}
547 	info.numberOfInfoStrings = 0;
548 
549 	// Not used!!!
550 	info.numberOfCommentStrings = 1;
551 #ifdef SID_HAVE_EXCEPTIONS
552 	info.commentString = new(std::nothrow) char* [info.numberOfCommentStrings];
553 #else
554 	info.commentString = new char* [info.numberOfCommentStrings];
555 #endif
556     if (info.commentString != 0)
557         info.commentString[0] = myStrDup("--- SAVED WITH SIDPLAY ---");
558     else
559         info.commentString[0] = 0;
560 }
561 
562 
safeDestructor()563 void sidTune::safeDestructor()
564 {
565 	// Remove copy of comment field.
566 	udword strNum = 0;
567 	// Check and remove every available line.
568 	while (info.numberOfCommentStrings-- > 0)
569 	{
570 		if (info.commentString[strNum] != 0)
571 		{
572 			delete[] info.commentString[strNum];
573 			info.commentString[strNum] = 0;
574 		}
575 		strNum++;  // next string
576 	};
577 	delete[] info.commentString;  // free the array pointer
578 
579 	clearCache();
580 	deleteFileNameCopies();
581 	deleteFileBuffers();
582 
583 	status = false;
584 }
585 
586 
587 #if !defined(SID_NO_STDIN_LOADER)
588 
stdinConstructor()589 void sidTune::stdinConstructor()
590 {
591 	// Assume a failure, so we can simply return.
592 	status = false;
593 	// Assume the memory allocation to fail.
594 	info.statusString = text_notEnoughMemory;
595 #ifdef SID_HAVE_EXCEPTIONS
596 	if (( fileBuf = new(std::nothrow) ubyte[maxSidtuneFileLen] ) == 0 )
597 #else
598 	if (( fileBuf = new ubyte[maxSidtuneFileLen] ) == 0 )
599 #endif
600     {
601 		return;
602     }
603 	udword i = 0;
604 	char datb;
605 	// We only read as much as fits in the buffer.
606 	// This way we avoid choking on huge data.
607 	while (cin.get(datb) && i<maxSidtuneFileLen)
608 		fileBuf[i++] = datb;
609 	info.dataFileLen = i;
610     getSidtuneFromFileBuffer(fileBuf,info.dataFileLen);
611 }
612 
613 #endif
614 
615 
bufferConstructor(const ubyte * data,udword dataLen)616 void sidTune::bufferConstructor(const ubyte* data, udword dataLen)
617 {
618 	// Assume a failure, so we can simply return.
619 	status = false;
620 	if (data != 0)
621 	{
622 		if (dataLen > maxSidtuneFileLen)
623 		{
624 			info.statusString = text_fileTooLong;
625 		}
626 		else
627 		{
628 			info.dataFileLen = dataLen;
629 			getSidtuneFromFileBuffer(data, dataLen);
630 		}
631 	}
632 }
633 
getSidtuneFromFileBuffer(const ubyte * buffer,udword bufferLen)634 bool sidTune::getSidtuneFromFileBuffer(const ubyte* buffer, udword bufferLen)
635 {
636 	bool foundFormat = false;
637 	// Here test for the possible single file formats. ------------------
638 	if ( PSID_fileSupport( buffer, bufferLen ))
639 	{
640 		foundFormat = true;
641 	}
642 	else if ( MUS_fileSupport( buffer, bufferLen ))
643 	{
644 		foundFormat = true;
645 	}
646 	else
647 	{
648 		// No further single-file-formats available. --------------------
649 		info.formatString = text_na;
650 		info.statusString = text_unrecognizedFormat;
651 		status = false;
652 	}
653 	if ( foundFormat )
654 	{
655 		status = true;
656 		info.statusString = text_noErrors;
657 		acceptSidTune("-","-",buffer,bufferLen);
658 	}
659 	return foundFormat;
660 }
661 
662 
acceptSidTune(const char * dataFileName,const char * infoFileName,const ubyte * dataBuf,udword dataLen)663 void sidTune::acceptSidTune(const char* dataFileName, const char* infoFileName,
664                             const ubyte* dataBuf, udword dataLen )
665 {
666 	deleteFileNameCopies();
667 	// Make a copy of the data file name and path, if available.
668 	if ( dataFileName != 0 )
669 	{
670 		info.path = myStrDup(dataFileName);
671 		if (isSlashedFileName)
672 		{
673 			info.dataFileName = myStrDup(slashedFileNameWithoutPath(info.path));
674 			*slashedFileNameWithoutPath(info.path) = 0;  // path only
675 		}
676 		else
677 		{
678             info.dataFileName = myStrDup(fileNameWithoutPath(info.path));
679             *fileNameWithoutPath(info.path) = 0;  // path only
680         }
681 		if ((info.path==0) || (info.dataFileName==0))
682 		{
683 			info.statusString = text_notEnoughMemory;
684 			status = false;
685 			return;
686 		}
687 	}
688 	// Make a copy of the info file name, if available.
689 	if ( infoFileName != 0 )
690 	{
691 		char* tmp = myStrDup(infoFileName);
692 		if (isSlashedFileName)
693 			info.infoFileName = myStrDup(slashedFileNameWithoutPath(tmp));
694 		else
695 			info.infoFileName = myStrDup(fileNameWithoutPath(tmp));
696 		if ((tmp==0) || (info.infoFileName==0))
697 		{
698 			info.statusString = text_notEnoughMemory;
699 			status = false;
700 			return;
701 		}
702 		delete[] tmp;
703 	}
704 	// Fix bad sidtune set up.
705 	if (info.songs > classMaxSongs)
706 		info.songs = classMaxSongs;
707 	else if (info.songs == 0)
708 		info.songs++;
709 	if (info.startSong > info.songs)
710 		info.startSong = 1;
711 	else if (info.startSong == 0)
712 		info.startSong++;
713 	info.dataFileLen = dataLen;
714 	info.c64dataLen = dataLen - fileOffset;
715 	cacheRawData( dataBuf, dataLen );
716 }
717 
718 
createNewFileName(char ** destStringPtr,const char * sourceName,const char * sourceExt)719 bool sidTune::createNewFileName( char** destStringPtr,
720                                  const char* sourceName,
721                                  const char* sourceExt)
722 {
723 	// Free any previously allocated object.
724 	if ( *destStringPtr != 0 )
725 	{
726 		delete[] *destStringPtr;
727 	}
728 	// Get enough memory, so we can appended the extension.
729 #ifdef SID_HAVE_EXCEPTIONS
730 	*destStringPtr = new(std::nothrow) char[strlen(sourceName) + strlen(sourceExt) +1];
731 #else
732 	*destStringPtr = new char[strlen(sourceName) + strlen(sourceExt) +1];
733 #endif
734 	if ( *destStringPtr == 0 )
735 	{
736 		info.statusString = text_notEnoughMemory;
737 		return (status = false);
738 	}
739 	strcpy( *destStringPtr, sourceName );
740 	char* extPtr = fileExtOfPath(*destStringPtr);
741 	strcpy( extPtr, sourceExt );
742 	return true;
743 }
744 
745 
746 // Initializing the object based upon what we find in the specified file.
747 
filesConstructor(const char * fileName)748 void sidTune::filesConstructor( const char* fileName )
749 {
750 	fileBuf = 0;  // will later point to the buffered file
751 	// Try to load the single specified file.
752 	if (( info.dataFileLen = loadFile(fileName,&fileBuf)) != 0 )
753 	{
754 		// File loaded. Now check if it is in a valid single-file-format.
755 		if ( PSID_fileSupport(fileBuf,info.dataFileLen ))
756 		{
757 			acceptSidTune(fileName,0,fileBuf,info.dataFileLen);
758 			return;
759 		}
760 		else if ( MUS_fileSupport(fileBuf,info.dataFileLen) )
761 		{
762 			acceptSidTune(fileName,0,fileBuf,info.dataFileLen);
763 			return;
764 		}
765 
766 
767 // -------------------------------------- Support for multiple-files formats.
768 		else
769 		{
770 // We cannot simply try to load additional files, if a description file was
771 // specified. It would work, but is error-prone. Imagine a filename mismatch
772 // or more than one description file (in another) format. Any other file
773 // with an appropriate file name can be the C64 data file.
774 // First we see if ``fileName'' could be a raw data file. In that case we
775 // have to find the corresponding description file.
776 
777 			// Right now we do not have a second file. Hence the (0, 0, ...)
778 			// parameters are set for the data buffer. This will not hurt the
779 			// file support procedures.
780 			udword fileLen2;
781 			char* fileName2 = 0;
782 
783 			// Make sure that ``fileBuf'' does not contain a description file.
784 			if ( !SID_fileSupport(0,0,fileBuf,info.dataFileLen) &&
785 				!INFO_fileSupport(0,0,fileBuf,info.dataFileLen) )
786 			{
787 				// Assuming ``fileName'' to hold the name of the raw data file,
788 				// we now create the name of a description file (=fileName2) by
789 				// appending various filename extensions.
790 
791 // ------------------------------------------ Looking for a description file.
792 
793 				const char **tmpFileNameExt = fileNameExtensions;
794 				while (*tmpFileNameExt != 0)
795 				{
796 					if ( !createNewFileName(&fileName2,fileName,*tmpFileNameExt) )
797 						return;
798 					// Do not load the first file again if names are equal.
799 					if ( myStrCaseCmp(fileName,fileName2) != 0 )
800 					{
801 						// 1st data file was loaded into ``fileBuf'',
802 						// so we load the 2nd one into ``fileBuf2''.
803 						if (( fileLen2 = loadFile(fileName2,&fileBuf2)) != 0 )
804 						{
805 							if ( SID_fileSupport(fileBuf,info.dataFileLen,fileBuf2,fileLen2) )
806 							{
807 								acceptSidTune(fileName,fileName2,fileBuf,info.dataFileLen);
808 								delete[] fileName2;
809 								return;
810 							}
811 							else if ( INFO_fileSupport(fileBuf,info.dataFileLen,fileBuf2,fileLen2) )
812 							{
813 								acceptSidTune(fileName,fileName2,fileBuf,info.dataFileLen);
814 								delete[] fileName2;
815 								return;
816 							}
817 						}
818 					}
819 					tmpFileNameExt++;
820 				};
821 
822 // --------------------------------------- Could not find a description file.
823 
824 				delete[] fileName2;
825 				info.formatString = text_na;
826 				info.statusString = text_unrecognizedFormat;
827 				status = false;
828 				return;
829 			}
830 
831 
832 // -------------------------------------------------------------------------
833 // Still unsuccessful ? Probably one put a description file name into
834 // ``fileName''. Assuming ``fileName'' to hold the name of a description
835 // file, we now create the name of the data file and swap both used memory
836 // buffers - fileBuf and fileBuf2 - when calling the format support.
837 // If it works, the second file is the data file ! If it is not, but does
838 // exist, we are out of luck, since we cannot detect data files.
839 
840 			// Make sure ``fileBuf'' contains a description file.
841 			else if ( SID_fileSupport(0,0,fileBuf,info.dataFileLen) ||
842 					 INFO_fileSupport(0,0,fileBuf,info.dataFileLen))
843 			{
844 
845 // --------------------- Description file found. --- Looking for a data file.
846 
847 				const char **tmpFileNameExt = fileNameExtensions;
848 				while (*tmpFileNameExt != 0)
849 				{
850 					if ( !createNewFileName(&fileName2,fileName,*tmpFileNameExt) )
851 						return;
852 					// Do not load the first file again if names are equal.
853 					if ( myStrCaseCmp(fileName,fileName2) != 0 )
854 					{
855 						// 1st info file was loaded into ``fileBuf'',
856 						// so we load the 2nd one into ``fileBuf2''.
857 						if (( fileLen2 = loadFile(fileName2,&fileBuf2)) != 0 )
858 						{
859 // -------------- Some data file found, now identifying the description file.
860 
861 							if ( SID_fileSupport(fileBuf2,fileLen2,fileBuf,info.dataFileLen) )
862 							{
863 								acceptSidTune(fileName2,fileName,fileBuf2,fileLen2);
864 								delete[] fileName2;
865 								return;
866 							}
867 							else if ( INFO_fileSupport(fileBuf2,fileLen2,fileBuf,info.dataFileLen) )
868 							{
869 								acceptSidTune(fileName2,fileName,fileBuf2,fileLen2);
870 								delete[] fileName2;
871 								return;
872 							}
873 						}
874 					}
875 					tmpFileNameExt++;
876 				};
877 
878 // ---------------------------------------- No corresponding data file found.
879 
880 				delete[] fileName2;
881 				info.formatString = text_na;
882 				info.statusString = text_noDataFile;
883 				status = false;
884 				return;
885 			} // end else if ( = is description file )
886 
887 // --------------------------------- Neither description nor data file found.
888 
889 			else
890 			{
891 				info.formatString = text_na;
892 				info.statusString = text_unrecognizedFormat;
893 				status = false;
894 				return;
895 			}
896 		} // end else ( = is no singlefile )
897 
898 // ---------------------------------------------------------- File I/O error.
899 
900 	} // if loaddatafile
901 	else
902 	{
903 		// returned fileLen was 0 = error. The info.statusString is
904 		// already set then.
905 		info.formatString = text_na;
906 		status = false;
907 		return;
908 	}
909 }
910 
911 
convertOldStyleSpeedToTables(udword oldStyleSpeed)912 void sidTune::convertOldStyleSpeedToTables(udword oldStyleSpeed)
913 {
914 	// Create the speed/clock setting tables.
915 	//
916 	// This does not take into account the PlaySID bug upon evaluating the
917 	// SPEED field. It would most likely break compatibility to lots of
918 	// sidtunes, which have been converted from .SID format and vice versa.
919 	// The .SID format does the bit-wise/song-wise evaluation of the SPEED
920 	// value correctly, like it is described in the PlaySID documentation.
921 
922 	int toDo = ((info.songs <= classMaxSongs) ? info.songs : classMaxSongs);
923 	for (int s = 0; s < toDo; s++)
924 	{
925 		if (( (oldStyleSpeed>>(s&31)) & 1 ) == 0 )
926 		{
927 			songSpeed[s] = SIDTUNE_SPEED_VBI;
928 		}
929 		else
930 		{
931 			songSpeed[s] = SIDTUNE_SPEED_CIA_1A;
932 		}
933         clockSpeed[s] = info.clock;
934 	}
935 }
936 
937 
938 //
939 // File format conversion ---------------------------------------------------
940 //
941 
saveToOpenFile(ofstream & toFile,const ubyte * buffer,udword bufLen)942 bool sidTune::saveToOpenFile( ofstream& toFile, const ubyte* buffer, udword bufLen )
943 {
944 	udword lenToWrite = bufLen;
945 	while ( lenToWrite > INT_MAX )
946 	{
947 		toFile.write( (const char*)buffer + (bufLen - lenToWrite), INT_MAX );
948 		lenToWrite -= INT_MAX;
949 	}
950 	if ( lenToWrite > 0 )
951 		toFile.write( (const char*)buffer + (bufLen - lenToWrite), lenToWrite );
952 	if ( toFile.bad() )
953 	{
954 		info.statusString = text_fileIoError;
955 		return false;
956 	}
957 	else
958 	{
959 		info.statusString = text_noErrors;
960 		return true;
961 	}
962 }
963 
964 
saveC64dataFile(const char * fileName,bool overWriteFlag)965 bool sidTune::saveC64dataFile( const char* fileName, bool overWriteFlag )
966 {
967 	bool success = false;  // assume error
968 	// This prevents saving from a bad object.
969 	if ( status )
970 	{
971 		ofstream fMyOut;
972         if ( !overWriteFlag && fileExists( fileName ) )
973         {
974 			info.statusString = text_cantCreateFile;
975             return success;
976         }
977 		// Open binary output file stream.
978         else
979 #if defined(SID_HAVE_IOS_BIN)
980             fMyOut.open( fileName, ios::out|ios::bin|ios::trunc );
981 #else
982             fMyOut.open( fileName, ios::out|ios::binary|ios::trunc );
983 #endif
984 		if ( !fMyOut )
985 		{
986 			info.statusString = text_cantCreateFile;
987 		}
988 		else
989 		{
990 			// Save c64 lo/hi load address.
991 			ubyte saveAddr[2];
992 			saveAddr[0] = info.loadAddr & 255;
993 			saveAddr[1] = info.loadAddr >> 8;
994 			fMyOut.write( (const char*)saveAddr, 2 );
995 			// Data starts at: bufferaddr + fileOffset
996 			// Data length: info.dataFileLen - fileOffset
997 			if ( !saveToOpenFile( fMyOut, cachePtr + fileOffset, info.dataFileLen - fileOffset ) )
998 			{
999 				info.statusString = text_fileIoError;
1000 			}
1001 			else
1002 			{
1003 				info.statusString = text_noErrors;
1004 				success = true;
1005 			}
1006 			fMyOut.close();
1007 		}
1008 	}
1009 	return success;
1010 }
1011 
1012 
saveSIDfile(const char * fileName,bool overWriteFlag)1013 bool sidTune::saveSIDfile( const char* fileName, bool overWriteFlag )
1014 {
1015 	bool success = false;  // assume error
1016 	// This prevents saving from a bad object.
1017 	if ( status )
1018 	{
1019 		ofstream fMyOut;
1020         if ( !overWriteFlag && fileExists( fileName ) )
1021         {
1022 			info.statusString = text_cantCreateFile;
1023             return success;
1024         }
1025 		// Open ASCII output file stream.
1026         else
1027 #if defined(SID_HAVE_IOS_BIN)
1028             fMyOut.open( fileName, ios::out|ios::trunc );
1029 #else
1030             fMyOut.open( fileName, ios::out|ios::trunc );
1031 #endif
1032 		if ( !fMyOut )
1033 		{
1034 			info.statusString = text_cantCreateFile;
1035 		}
1036 		else
1037 		{
1038 			if ( !SID_fileSupportSave( fMyOut ) )
1039 			{
1040 				info.statusString = text_fileIoError;
1041 			}
1042 			else
1043 			{
1044 				info.statusString = text_noErrors;
1045 				success = true;
1046 			}
1047 			fMyOut.close();
1048 		}
1049 	}
1050 	return success;
1051 }
1052 
1053 
savePSIDfile(const char * fileName,bool overWriteFlag)1054 bool sidTune::savePSIDfile( const char* fileName, bool overWriteFlag )
1055 {
1056 	bool success = false;  // assume error
1057 	// This prevents saving from a bad object.
1058 	if ( status )
1059 	{
1060 		ofstream fMyOut;
1061         if ( !overWriteFlag && fileExists( fileName ) )
1062         {
1063 			info.statusString = text_cantCreateFile;
1064             return success;
1065         }
1066 		// Open binary output file stream.
1067         else
1068 #if defined(SID_HAVE_IOS_BIN)
1069             fMyOut.open( fileName, ios::out|ios::bin|ios::trunc );
1070 #else
1071             fMyOut.open( fileName, ios::out|ios::binary|ios::trunc );
1072 #endif
1073 		if ( !fMyOut )
1074 		{
1075 			info.statusString = text_cantCreateFile;
1076 		}
1077 		else
1078 		{
1079 			if ( !PSID_fileSupportSave( fMyOut, cachePtr ) )
1080 			{
1081 				info.statusString = text_fileIoError;
1082 			}
1083 			else
1084 			{
1085 				info.statusString = text_noErrors;
1086 				success = true;
1087 			}
1088 			fMyOut.close();
1089 		}
1090 	}
1091 	return success;
1092 }
1093