#include "stdafx.h" #include "NsfLoader.h" #include "RomData.h" #include "MapperFactory.h" void NsfLoader::Read(uint8_t *& data, uint8_t & dest) { dest = data[0]; data++; } void NsfLoader::Read(uint8_t *& data, uint16_t & dest) { dest = data[0] | (data[1] << 8); data += 2; } void NsfLoader::Read(uint8_t *& data, char * dest, size_t len) { memcpy(dest, data, len); data += len; } void NsfLoader::InitializeFromHeader(RomData &romData) { NsfHeader &header = romData.Info.NsfInfo; romData.Info.Format = RomFormat::Nsf; romData.Info.MapperID = MapperFactory::NsfMapperID; if(header.LoadAddress < 0x6000 || header.TotalSongs == 0) { romData.Error = true; } if(header.Flags == 0x01) { romData.Info.System = GameSystem::NesPal; } if(header.PlaySpeedNtsc == 0) { header.PlaySpeedNtsc = 16639; } if(header.PlaySpeedPal == 0) { header.PlaySpeedPal = 19997; } if(header.StartingSong > header.TotalSongs || header.StartingSong == 0) { header.StartingSong = 1; } //Log window output Log("[NSF] Region: " + string(header.Flags == 0x00 ? "NTSC" : (header.Flags == 0x01 ? "PAL" : "NTSC & PAL"))); if(header.PlaySpeedNtsc > 0) { Log("[NSF] Play speed (NTSC): " + std::to_string(1000000.0 / (double)header.PlaySpeedNtsc) + " Hz"); } if(header.PlaySpeedPal > 0) { Log("[NSF] Play speed (PAL): " + std::to_string(1000000.0 / (double)header.PlaySpeedPal) + " Hz"); } Log("[NSF] Title: " + string(header.SongName)); Log("[NSF] Artist: " + string(header.ArtistName)); Log("[NSF] Copyright: " + string(header.CopyrightHolder)); Log("[NSF] Ripper: " + string(header.RipperName)); stringstream ss; ss << "[NSF] Load Address: 0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << header.LoadAddress; Log(ss.str()); ss = stringstream(); ss << "[NSF] Init Address: 0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << header.InitAddress; Log(ss.str()); ss = stringstream(); ss << "[NSF] Play Address: 0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << header.PlayAddress; Log(ss.str()); vector chips; if(header.SoundChips & 0x01) { chips.push_back("VRC6"); } if(header.SoundChips & 0x02) { chips.push_back("VRC7"); } if(header.SoundChips & 0x04) { chips.push_back("FDS"); } if(header.SoundChips & 0x08) { chips.push_back("MMC5"); } if(header.SoundChips & 0x10) { chips.push_back("Namco 163"); } if(header.SoundChips & 0x20) { chips.push_back("Sunsoft 5B"); } if(chips.empty()) { chips.push_back(""); } ss = stringstream(); for(size_t i = 0; i < chips.size(); i++) { if(i > 0) { ss << ", "; } ss << chips[i]; } Log("[NSF] Sound Chips: " + ss.str()); Log("[NSF] ROM size: " + std::to_string(romData.PrgRom.size() / 1024) + " KB"); } void NsfLoader::InitHeader(NsfHeader & header) { memset(&header, 0, sizeof(NsfHeader)); for(int i = 0; i < 256; i++) { //Used by NSFE header.TrackLength[i] = -1; header.TrackFade[i] = -1; } } void NsfLoader::LoadRom(RomData& romData, vector& romFile) { NsfHeader &header = romData.Info.NsfInfo; InitHeader(header); uint8_t* data = romFile.data(); Read(data, header.Header, 5); Read(data, header.Version); Read(data, header.TotalSongs); Read(data, header.StartingSong); Read(data, header.LoadAddress); Read(data, header.InitAddress); Read(data, header.PlayAddress); Read(data, header.SongName, 32); Read(data, header.ArtistName, 32); Read(data, header.CopyrightHolder, 32); Read(data, header.PlaySpeedNtsc); Read(data, (char*)header.BankSetup, 8); Read(data, header.PlaySpeedPal); Read(data, header.Flags); Read(data, header.SoundChips); Read(data, (char*)header.Padding, 4); //Ensure strings are null terminated header.SongName[31] = 0; header.ArtistName[31] = 0; header.CopyrightHolder[31] = 0; if(header.PlaySpeedNtsc == 16666 || header.PlaySpeedNtsc == 16667) { header.PlaySpeedNtsc = 16639; } if(header.PlaySpeedPal == 20000) { header.PlaySpeedPal = 19997; } //Pad start of file to make the first block start at a multiple of 4k romData.PrgRom.insert(romData.PrgRom.end(), header.LoadAddress % 4096, 0); romData.PrgRom.insert(romData.PrgRom.end(), data, data + romFile.size() - 0x80); //Pad out the last block to be a multiple of 4k if(romData.PrgRom.size() % 4096 != 0) { romData.PrgRom.insert(romData.PrgRom.end(), 4096 - (romData.PrgRom.size() % 4096), 0); } //NSF header size romData.Info.FilePrgOffset = 0x80; InitializeFromHeader(romData); }