Mesen/Core/NsfeLoader.h
Sour 8047fe44bb Added Study Box tape support (.studybox files)
-Added audio support
-Improved emulation logic
-Games are loaded via .studybox files
2019-12-31 20:24:06 -05:00

208 lines
5 KiB
C++

#pragma once
#include "stdafx.h"
#include <cstring>
#include <algorithm>
#include "RomData.h"
#include "NsfLoader.h"
class NsfeLoader : public NsfLoader
{
private:
void CopyString(char* dest, string source, int maxLength)
{
memset(dest, 0, maxLength);
memcpy(dest, source.c_str(), std::min(source.size(), (size_t)maxLength - 1));
}
void Read(uint8_t* &data, uint8_t& dest)
{
dest = data[0];
data++;
}
void Read(uint8_t* &data, uint16_t& dest)
{
dest = data[0] | (data[1] << 8);
data += 2;
}
void Read(uint8_t* &data, uint32_t& dest)
{
dest = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
data += 4;
}
void Read(uint8_t* &data, int32_t& dest)
{
dest = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
data += 4;
}
void Read(uint8_t* &data, char* dest, size_t len)
{
memcpy(dest, data, len);
data += len;
}
vector<string> ReadStrings(uint8_t* &data, uint8_t* chunkEnd)
{
vector<string> strings;
stringstream ss;
while(data < chunkEnd) {
if(data[0] == 0) {
//end of string
strings.push_back(ss.str());
ss = stringstream();
} else {
ss << (char)data[0];
}
data++;
}
//truncate all strings to 255 characters + null
for(size_t i = 0; i < strings.size(); i++) {
strings[i] = strings[i].substr(0, std::min((int)strings[i].size(), 255));
}
return strings;
}
string ReadFourCC(uint8_t* &data)
{
stringstream ss;
for(int i = 0; i < 4; i++) {
ss << (char)data[i];
}
data += 4;
return ss.str();
}
bool ReadChunk(uint8_t* &data, uint8_t* dataEnd, RomData& romData, uint8_t* fileStart)
{
if(data + 4 > dataEnd) {
return false;
}
NsfHeader& header = romData.Info.NsfInfo;
uint32_t length;
Read(data, length);
uint8_t* chunkEnd = data + 4 + length;
if(chunkEnd > dataEnd) {
return false;
}
string fourCC = ReadFourCC(data);
if(fourCC.compare("INFO") == 0) {
Read(data, header.LoadAddress);
Read(data, header.InitAddress);
Read(data, header.PlayAddress);
Read(data, header.Flags);
Read(data, header.SoundChips);
Read(data, header.TotalSongs);
Read(data, header.StartingSong);
header.PlaySpeedNtsc = 16639;
header.PlaySpeedPal = 19997;
//Adjust to match NSF spec
header.StartingSong++;
} else if(fourCC.compare("DATA") == 0) {
romData.Info.FilePrgOffset = (int32_t)(data - fileStart);
//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(), (uint8_t*)data, data+length);
//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);
}
} else if(fourCC.compare("NEND") == 0) {
//End of file
romData.Error = false;
return false;
} else if(fourCC.compare("BANK") == 0) {
memset(header.BankSetup, 0, sizeof(header.BankSetup));
Read(data, (char*)header.BankSetup, std::min(8, (int)length));
} else if(fourCC.compare("plst") == 0) {
//not supported
} else if(fourCC.compare("time") == 0) {
int i = 0;
while(data < chunkEnd) {
Read(data, header.TrackLength[i]);
i++;
}
} else if(fourCC.compare("fade") == 0) {
int i = 0;
while(data < chunkEnd) {
Read(data, header.TrackFade[i]);
i++;
}
} else if(fourCC.compare("tlbl") == 0) {
vector<string> trackNames = ReadStrings(data, chunkEnd);
stringstream ss;
for(string &trackName : trackNames) {
ss << trackName;
ss << "[!|!]";
}
CopyString(header.TrackName, ss.str(), 20000);
} else if(fourCC.compare("auth") == 0) {
vector<string> infoStrings = ReadStrings(data, chunkEnd);
if(infoStrings.size() > 0) {
CopyString(header.SongName, infoStrings[0], 256);
if(infoStrings.size() > 1) {
CopyString(header.ArtistName, infoStrings[1], 256);
if(infoStrings.size() > 2) {
CopyString(header.CopyrightHolder, infoStrings[2], 256);
if(infoStrings.size() > 3) {
CopyString(header.RipperName, infoStrings[3], 256);
}
}
}
}
} else if(fourCC.compare("text") == 0) {
//not supported
} else {
if(fourCC[0] >= 'A' && fourCC[0] <= 'Z') {
//unknown required block, can't read file
return false;
}
}
data = chunkEnd;
return true;
}
public:
using NsfLoader::NsfLoader;
void LoadRom(RomData& romData, vector<uint8_t>& romFile)
{
NsfHeader &header = romData.Info.NsfInfo;
InitHeader(header);
romData.Info.Format = RomFormat::Nsf;
uint8_t* fileStart = romFile.data();
uint8_t* data = romFile.data() + 4;
uint8_t* endOfData = romFile.data() + romFile.size();
memset(header.SongName, 0, sizeof(header.SongName));
memset(header.ArtistName, 0, sizeof(header.ArtistName));
memset(header.CopyrightHolder, 0, sizeof(header.CopyrightHolder));
//Will be set to false when we read NEND block
romData.Error = true;
while(ReadChunk(data, endOfData, romData, fileStart)) {
//Read all chunks
}
InitializeFromHeader(romData);
}
};