summaryrefslogtreecommitdiff
path: root/sid/sidplay-libs-2.1.0/libsidplay/src/sidtune/PSID.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sid/sidplay-libs-2.1.0/libsidplay/src/sidtune/PSID.cpp')
-rw-r--r--sid/sidplay-libs-2.1.0/libsidplay/src/sidtune/PSID.cpp313
1 files changed, 313 insertions, 0 deletions
diff --git a/sid/sidplay-libs-2.1.0/libsidplay/src/sidtune/PSID.cpp b/sid/sidplay-libs-2.1.0/libsidplay/src/sidtune/PSID.cpp
new file mode 100644
index 00000000..0cc96969
--- /dev/null
+++ b/sid/sidplay-libs-2.1.0/libsidplay/src/sidtune/PSID.cpp
@@ -0,0 +1,313 @@
+/*
+ * /home/ms/files/source/libsidtune/RCS/PSID.cpp,v
+ *
+ * PlaySID one-file format support.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "SidTuneCfg.h"
+#include "SidTune.h"
+#include "sidendian.h"
+
+#define PSID_ID 0x50534944
+#define RSID_ID 0x52534944
+
+// Header has been extended for 'RSID' format
+// The following changes are present:
+// id = 'RSID'
+// version = 2 only
+// play, load and speed reserved 0
+// psidspecific flag reserved 0
+// init cannot be under ROMS/IO
+// load cannot be less than 0x0801 (start of basic)
+
+struct psidHeader // all values big-endian
+{
+ char id[4]; // 'PSID' (ASCII)
+ uint8_t version[2]; // 0x0001 or 0x0002
+ uint8_t data[2]; // 16-bit offset to binary data in file
+ uint8_t load[2]; // 16-bit C64 address to load file to
+ uint8_t init[2]; // 16-bit C64 address of init subroutine
+ uint8_t play[2]; // 16-bit C64 address of play subroutine
+ uint8_t songs[2]; // number of songs
+ uint8_t start[2]; // start song out of [1..256]
+ uint8_t speed[4]; // 32-bit speed info
+ // bit: 0=50 Hz, 1=CIA 1 Timer A (default: 60 Hz)
+ char name[32]; // ASCII strings, 31 characters long and
+ char author[32]; // terminated by a trailing zero
+ char released[32]; //
+ uint8_t flags[2]; // only version 0x0002
+ uint8_t relocStartPage; // only version 0x0002B
+ uint8_t relocPages; // only version 0x0002B
+ uint8_t reserved[2]; // only version 0x0002
+};
+
+enum
+{
+ PSID_MUS = 1 << 0,
+ PSID_SPECIFIC = 1 << 1,
+ PSID_CLOCK = 3 << 2,
+ PSID_SIDMODEL = 3 << 4
+};
+
+enum
+{
+ PSID_CLOCK_UNKNOWN = 0,
+ PSID_CLOCK_PAL = 1 << 2,
+ PSID_CLOCK_NTSC = 1 << 3,
+ PSID_CLOCK_ANY = PSID_CLOCK_PAL | PSID_CLOCK_NTSC
+};
+
+enum
+{
+ PSID_SIDMODEL_UNKNOWN = 0,
+ PSID_SIDMODEL_6581 = 1 << 4,
+ PSID_SIDMODEL_8580 = 1 << 5,
+ PSID_SIDMODEL_ANY = PSID_SIDMODEL_6581 | PSID_SIDMODEL_8580
+};
+
+static const char _sidtune_format_psid[] = "PlaySID one-file format (PSID)";
+static const char _sidtune_format_rsid[] = "Real C64 one-file format (RSID)";
+static const char _sidtune_unknown_psid[] = "Unsupported PSID version";
+static const char _sidtune_unknown_rsid[] = "Unsupported RSID version";
+static const char _sidtune_truncated[] = "ERROR: File is most likely truncated";
+static const char _sidtune_invalid[] = "ERROR: File contains invalid data";
+
+const int _sidtune_psid_maxStrLen = 31;
+
+
+bool SidTune::PSID_fileSupport(const void* buffer, const uint_least32_t bufLen)
+{
+ int clock, compatibility;
+ uint_least32_t speed;
+#ifdef SIDTUNE_PSID2NG
+ clock = SIDTUNE_CLOCK_UNKNOWN;
+#else
+ clock = info.clockSpeed;
+#endif
+ compatibility = SIDTUNE_COMPATIBILITY_C64;
+
+ // Require minimum size to allow access to the first few bytes.
+ // Require a valid ID and version number.
+ const psidHeader* pHeader = (const psidHeader*)buffer;
+
+ // File format check
+ if (bufLen<6)
+ return false;
+ if (endian_big32((const uint_least8_t*)pHeader->id)==PSID_ID)
+ {
+ if (endian_big16(pHeader->version) >= 3)
+ {
+ info.formatString = _sidtune_unknown_psid;
+ return false;
+ }
+ info.formatString = _sidtune_format_psid;
+ }
+ else if (endian_big32((const uint_least8_t*)pHeader->id)==RSID_ID)
+ {
+ if (endian_big16(pHeader->version) != 2)
+ {
+ info.formatString = _sidtune_unknown_rsid;
+ return false;
+ }
+ info.formatString = _sidtune_format_rsid;
+ compatibility = SIDTUNE_COMPATIBILITY_R64;
+ }
+ else
+ {
+ return false;
+ }
+
+ // Due to security concerns, input must be at least as long as version 1
+ // header plus 16-bit C64 load address. That is the area which will be
+ // accessed.
+ if ( bufLen < (sizeof(psidHeader)+2) )
+ {
+ info.formatString = _sidtune_truncated;
+ return false;
+ }
+
+ fileOffset = endian_big16(pHeader->data);
+ info.loadAddr = endian_big16(pHeader->load);
+ info.initAddr = endian_big16(pHeader->init);
+ info.playAddr = endian_big16(pHeader->play);
+ info.songs = endian_big16(pHeader->songs);
+ info.startSong = endian_big16(pHeader->start);
+ info.sidChipBase1 = 0xd400;
+ info.sidChipBase2 = 0;
+ info.compatibility = compatibility;
+ speed = endian_big32(pHeader->speed);
+
+ if (info.songs > SIDTUNE_MAX_SONGS)
+ {
+ info.songs = SIDTUNE_MAX_SONGS;
+ }
+
+ info.musPlayer = false;
+ info.sidModel = SIDTUNE_SIDMODEL_UNKNOWN;
+ info.relocPages = 0;
+ info.relocStartPage = 0;
+ if ( endian_big16(pHeader->version) >= 2 )
+ {
+ uint_least16_t flags = endian_big16(pHeader->flags);
+ if (flags & PSID_MUS)
+ { // MUS tunes run at any speed
+ clock = SIDTUNE_CLOCK_ANY;
+ info.musPlayer = true;
+ }
+
+#ifdef SIDTUNE_PSID2NG
+ if (flags & PSID_SPECIFIC)
+ info.compatibility = SIDTUNE_COMPATIBILITY_PSID;
+
+ if (flags & PSID_CLOCK_PAL)
+ clock |= SIDTUNE_CLOCK_PAL;
+ if (flags & PSID_CLOCK_NTSC)
+ clock |= SIDTUNE_CLOCK_NTSC;
+ info.clockSpeed = clock;
+
+ info.sidModel = SIDTUNE_SIDMODEL_UNKNOWN;
+ if (flags & PSID_SIDMODEL_6581)
+ info.sidModel |= SIDTUNE_SIDMODEL_6581;
+ if (flags & PSID_SIDMODEL_8580)
+ info.sidModel |= SIDTUNE_SIDMODEL_8580;
+
+ info.relocStartPage = pHeader->relocStartPage;
+ info.relocPages = pHeader->relocPages;
+#endif // SIDTUNE_PSID2NG
+ }
+
+ // Reserved meaning for possible future use
+ if ( info.playAddr == 0xffff )
+ info.playAddr = 0;
+
+ // Check reserved fields to force real c64 compliance
+ if (compatibility == SIDTUNE_COMPATIBILITY_R64)
+ {
+ if (checkRealC64Info (speed) == false)
+ {
+ info.formatString = _sidtune_invalid;
+ return false;
+ }
+ // Real C64 tunes appear as CIA
+ speed = ~0;
+ }
+ // Create the speed/clock setting table.
+ convertOldStyleSpeedToTables(speed, clock);
+
+ if ( info.loadAddr == 0 )
+ {
+ uint_least8_t* pData = (uint_least8_t*)buffer + fileOffset;
+ info.loadAddr = endian_16( *(pData+1), *pData );
+ fileOffset += 2;
+ }
+
+ info.c64dataLen = bufLen - fileOffset;
+ if ( resolveAddrs((uint_least8_t*)buffer + fileOffset) == false )
+ return false;
+
+ if ( checkRelocInfo() == false )
+ return false;
+
+ // Copy info strings, so they will not get lost.
+ info.numberOfInfoStrings = 3;
+ // Name
+ strncpy(&infoString[0][0],pHeader->name,_sidtune_psid_maxStrLen);
+ info.infoString[0] = &infoString[0][0];
+ // Author
+ strncpy(&infoString[1][0],pHeader->author,_sidtune_psid_maxStrLen);
+ info.infoString[1] = &infoString[1][0];
+ // Released
+ strncpy(&infoString[2][0],pHeader->released,_sidtune_psid_maxStrLen);
+ info.infoString[2] = &infoString[2][0];
+ return true;
+}
+
+
+bool SidTune::PSID_fileSupportSave(std::ofstream& fMyOut, const uint_least8_t* dataBuffer)
+{
+ psidHeader myHeader;
+ endian_big32((uint_least8_t*)myHeader.id,PSID_ID);
+ endian_big16(myHeader.version,2);
+ endian_big16(myHeader.data,sizeof(psidHeader));
+ endian_big16(myHeader.load,0);
+ endian_big16(myHeader.init,info.initAddr);
+ endian_big16(myHeader.play,info.playAddr);
+ endian_big16(myHeader.songs,info.songs);
+ endian_big16(myHeader.start,info.startSong);
+
+ uint_least32_t speed = 0, check = 0;
+ uint_least32_t maxBugSongs = ((info.songs <= 32) ? info.songs : 32);
+ for (uint_least32_t s = 0; s < maxBugSongs; s++)
+ {
+ if (songSpeed[s] == SIDTUNE_SPEED_CIA_1A)
+ speed |= (1<<s);
+ check |= (1<<s);
+ }
+ endian_big32(myHeader.speed,speed);
+
+ uint_least16_t tmpFlags = 0;
+ if ( info.musPlayer )
+ tmpFlags |= PSID_MUS;
+
+ if (info.compatibility == SIDTUNE_COMPATIBILITY_PSID)
+ tmpFlags |= PSID_SPECIFIC;
+
+ tmpFlags |= (info.clockSpeed << 2);
+ tmpFlags |= (info.sidModel << 4);
+ endian_big16(myHeader.flags,tmpFlags);
+ endian_big16(myHeader.reserved,0);
+ myHeader.relocStartPage = info.relocStartPage;
+ myHeader.relocPages = info.relocPages;
+
+ for ( uint i = 0; i < 32; i++ )
+ {
+ myHeader.name[i] = 0;
+ myHeader.author[i] = 0;
+ myHeader.released[i] = 0;
+ }
+ strncpy( myHeader.name, info.infoString[0], _sidtune_psid_maxStrLen);
+ strncpy( myHeader.author, info.infoString[1], _sidtune_psid_maxStrLen);
+ strncpy( myHeader.released, info.infoString[2], _sidtune_psid_maxStrLen);
+
+ if (info.compatibility == SIDTUNE_COMPATIBILITY_R64)
+ {
+ endian_big32((uint_least8_t*)myHeader.id,RSID_ID);
+ endian_big16(myHeader.play,0);
+ endian_big32(myHeader.speed,0);
+ }
+
+ fMyOut.write( (char*)&myHeader, sizeof(psidHeader) );
+
+ // Save C64 lo/hi load address (little-endian).
+ uint_least8_t saveAddr[2];
+ saveAddr[0] = info.loadAddr & 255;
+ saveAddr[1] = info.loadAddr >> 8;
+ fMyOut.write( (char*)saveAddr, 2 ); // !cast!
+
+ // Data starts at: bufferaddr + fileoffset
+ // Data length: datafilelen - fileoffset
+ fMyOut.write( (const char*)dataBuffer + fileOffset, info.dataFileLen - fileOffset ); // !cast!
+ if ( !fMyOut )
+ return false;
+ else
+ return true;
+}