public static SidTuneBase load(ref byte[] dataBuf) { // File format check if (dataBuf.Length < 4) { return(null); } UInt32 magic = sidendian.endian_big32(dataBuf); if ((magic != PSID_ID) && (magic != RSID_ID)) { return(null); } psidHeader pHeader = new psidHeader(); readHeader(dataBuf, ref pHeader); PSID tune = new PSID(); tune.tryLoad(ref pHeader); return(tune); }
private static void readHeader(byte[] dataBuf, ref psidHeader hdr) { // 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 (dataBuf.Length < (psid_headerSize + 2)) { throw new loadError(ERR_TRUNCATED); } // Read v1 fields hdr.id = sidendian.endian_big32(new Ptr <byte>(dataBuf, 0)); hdr.version = sidendian.endian_big16(new Ptr <byte>(dataBuf, 4)); hdr.data = sidendian.endian_big16(new Ptr <byte>(dataBuf, 6)); hdr.load = sidendian.endian_big16(new Ptr <byte>(dataBuf, 8)); hdr.init = sidendian.endian_big16(new Ptr <byte>(dataBuf, 10)); hdr.play = sidendian.endian_big16(new Ptr <byte>(dataBuf, 12)); hdr.songs = sidendian.endian_big16(new Ptr <byte>(dataBuf, 14)); hdr.start = sidendian.endian_big16(new Ptr <byte>(dataBuf, 16)); hdr.speed = sidendian.endian_big32(new Ptr <byte>(dataBuf, 18)); mem.memcpy(ref hdr.name, new Ptr <byte>(dataBuf, 22), PSID_MAXSTRLEN); mem.memcpy(ref hdr.author, new Ptr <byte>(dataBuf, 54), PSID_MAXSTRLEN); mem.memcpy(ref hdr.released, new Ptr <byte>(dataBuf, 86), PSID_MAXSTRLEN); if (hdr.version >= 2) { if (dataBuf.Length < (psidv2_headerSize + 2)) { throw new loadError(ERR_TRUNCATED); } // Read v2/3/4 fields hdr.flags = sidendian.endian_big16(new Ptr <byte>(dataBuf, 118)); hdr.relocStartPage = dataBuf[120]; hdr.relocPages = dataBuf[121]; hdr.sidChipBase2 = dataBuf[122]; hdr.sidChipBase3 = dataBuf[123]; } }
private void tryLoad(ref psidHeader pHeader) { SidTuneInfo.compatibility_t compatibility = SidTuneInfo.compatibility_t.COMPATIBILITY_C64; // Require a valid ID and version number. if (pHeader.id == PSID_ID) { switch (pHeader.version) { case 1: compatibility = SidTuneInfo.compatibility_t.COMPATIBILITY_PSID; break; case 2: case 3: case 4: break; default: throw new loadError(TXT_UNKNOWN_PSID); } info.m_formatString = TXT_FORMAT_PSID; } else if (pHeader.id == RSID_ID) { switch (pHeader.version) { case 2: case 3: case 4: break; default: throw new loadError(TXT_UNKNOWN_RSID); } info.m_formatString = TXT_FORMAT_RSID; compatibility = SidTuneInfo.compatibility_t.COMPATIBILITY_R64; } fileOffset = pHeader.data; info.m_loadAddr = pHeader.load; info.m_initAddr = pHeader.init; info.m_playAddr = pHeader.play; info.m_songs = pHeader.songs; info.m_startSong = pHeader.start; info.m_compatibility = compatibility; info.m_relocPages = 0; info.m_relocStartPage = 0; UInt32 speed = pHeader.speed; SidTuneInfo.clock_t clock = SidTuneInfo.clock_t.CLOCK_UNKNOWN; bool musPlayer = false; if (pHeader.version >= 2) { UInt16 flags = pHeader.flags; // Check clock if ((flags & (UInt16)Psid.PSID_MUS) != 0) { // MUS tunes run at any speed clock = SidTuneInfo.clock_t.CLOCK_ANY; musPlayer = true; } else { switch ((flags & (UInt16)Psid.PSID_CLOCK)) { case (UInt16)Psid_Clock.PSID_CLOCK_ANY: clock = SidTuneInfo.clock_t.CLOCK_ANY; break; case (UInt16)Psid_Clock.PSID_CLOCK_PAL: clock = SidTuneInfo.clock_t.CLOCK_PAL; break; case (UInt16)Psid_Clock.PSID_CLOCK_NTSC: clock = SidTuneInfo.clock_t.CLOCK_NTSC; break; default: break; } } // These flags are only available for the appropriate // file formats switch (compatibility) { case SidTuneInfo.compatibility_t.COMPATIBILITY_C64: if ((flags & (UInt16)Psid.PSID_SPECIFIC) != 0) { info.m_compatibility = SidTuneInfo.compatibility_t.COMPATIBILITY_PSID; } break; case SidTuneInfo.compatibility_t.COMPATIBILITY_R64: if ((flags & (UInt16)Psid.PSID_BASIC) != 0) { info.m_compatibility = SidTuneInfo.compatibility_t.COMPATIBILITY_BASIC; } break; default: break; } info.m_clockSpeed = clock; info.m_sidModels[0] = getSidModel((UInt16)(flags >> 4)); info.m_relocStartPage = pHeader.relocStartPage; info.m_relocPages = pHeader.relocPages; if (pHeader.version >= 3) { if (validateAddress(pHeader.sidChipBase2)) { info.m_sidChipAddresses.Add((UInt16)(0xd000 | (pHeader.sidChipBase2 << 4))); info.m_sidModels.Add(getSidModel((UInt16)(flags >> 6))); } if (pHeader.version >= 4) { if (pHeader.sidChipBase3 != pHeader.sidChipBase2 && validateAddress(pHeader.sidChipBase3)) { info.m_sidChipAddresses.Add((UInt16)(0xd000 | (pHeader.sidChipBase3 << 4))); info.m_sidModels.Add((SidTuneInfo.model_t)(getSidModel((UInt16)(flags >> 8)))); } } } } // Check reserved fields to force real c64 compliance // as required by the RSID specification if (compatibility == SidTuneInfo.compatibility_t.COMPATIBILITY_R64) { if ((info.m_loadAddr != 0) || (info.m_playAddr != 0) || (speed != 0)) { throw new loadError(ERR_INVALID); } // Real C64 tunes appear as CIA speed = 0xffffffff;// ~0; } // Create the speed/clock setting table. convertOldStyleSpeedToTables(speed, (Int64)clock); // Copy info strings. info.m_infoString.Add(Encoding.ASCII.GetString(pHeader.name, 0, PSID_MAXSTRLEN)); info.m_infoString.Add(Encoding.ASCII.GetString(pHeader.author, 0, PSID_MAXSTRLEN)); info.m_infoString.Add(Encoding.ASCII.GetString(pHeader.released, 0, PSID_MAXSTRLEN)); if (musPlayer) { throw new loadError("Compute!'s Sidplayer MUS data is not supported yet"); // TODO } }