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