gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

xtRomLoader.cpp (5743B)


      1 #include "xtRomLoader.h"
      2 
      3 #include <cassert>
      4 #include <map>
      5 #include <algorithm>
      6 #include <cstring>
      7 
      8 #include "baseLib/filesystem.h"
      9 
     10 #include "wLib/wRom.h"
     11 
     12 namespace xt
     13 {
     14 	constexpr uint32_t g_1Kb = 1024;
     15 
     16 	constexpr uint32_t g_romSizeFull = 256 * g_1Kb;
     17 
     18 	constexpr uint32_t g_romSizeHalf = 128 * g_1Kb;
     19 
     20 	constexpr uint32_t g_midiSizeMin = 166 * g_1Kb;
     21 	constexpr uint32_t g_midiSizeMax = 171 * g_1Kb;
     22 
     23 	Rom RomLoader::findROM()
     24 	{
     25 		std::vector<File> allFiles;
     26 
     27 		{
     28 			// full roms are 256k in size
     29 			auto filesFull = findFiles(".bin", g_romSizeFull, g_romSizeFull);
     30 
     31 			for (auto& file : filesFull)
     32 			{
     33 				if(detectFileType(file) && detectVersion(file))
     34 					allFiles.push_back(std::move(file));
     35 			}
     36 		}
     37 
     38 		{
     39 			// half roms are 128k in size, we need exactly two files to combine them
     40 			auto filesHalf = findFiles(".bin", g_romSizeHalf, g_romSizeHalf);
     41 
     42 			for(uint32_t i=0; i<static_cast<uint32_t>(filesHalf.size());)
     43 			{
     44 				auto& file = filesHalf[i];
     45 				if(!detectFileType(file) || (file.type != FileType::HalfRomA && file.type != FileType::HalfRomB))
     46 					filesHalf.erase(filesHalf.begin() + i);
     47 				else
     48 					++i;
     49 			}
     50 
     51 			if(filesHalf.size() > 2)
     52 			{
     53 				// remove possible duplicates
     54 				for(size_t i=0; i<filesHalf.size(); ++i)
     55 				{
     56 					for(size_t j=i+1; j<filesHalf.size(); ++j)
     57 					{
     58 						if(filesHalf[i].data == filesHalf[j].data)
     59 						{
     60 							filesHalf.erase(filesHalf.begin() + static_cast<ptrdiff_t>(j));
     61 							--j;
     62 						}
     63 					}
     64 				}
     65 			}
     66 
     67 			if(filesHalf.size() == 2)
     68 			{
     69 				File& a = filesHalf.front();
     70 				File& b = filesHalf.back();
     71 
     72 				if(a.type == FileType::HalfRomB && b.type == FileType::HalfRomA)
     73 					std::swap(a,b);
     74 
     75 				if(a.type == FileType::HalfRomA && b.type == FileType::HalfRomB)
     76 				{
     77 					File result;
     78 					result.data.reserve(g_romSizeFull);
     79 
     80 					for(size_t i=0; i<g_romSizeHalf; ++i)
     81 					{
     82 						result.data.push_back(a.data[i]);
     83 						result.data.push_back(b.data[i]);
     84 					}
     85 
     86 					assert(result.data[0] == 0xc0 && result.data[1] == 0xde);
     87 
     88 					result.name = a.name + '_' + b.name;
     89 					result.type = FileType::FullRom;
     90 
     91 					if(detectVersion(result))
     92 						allFiles.push_back(std::move(result));
     93 				}
     94 			}
     95 		}
     96 
     97 		{
     98 			// midi file OS update contain about half of the rom, wavetables are missing so an OS update cannot
     99 			// be used on its own, but it can be used to upgrade an older rom by replacing the upper half with
    100 			// the content of the midi OS update
    101 			auto filesMidi = findFiles(".mid", g_midiSizeMin, g_midiSizeMax);
    102 
    103 			for (auto& file : filesMidi)
    104 			{
    105 				std::vector<uint8_t> data;
    106 				if(!wLib::ROM::loadFromMidiData(data, file.data))
    107 					continue;
    108 				if(!removeBootloader(data))
    109 					continue;
    110 				file.data = data;
    111 				if(detectFileType(file) && detectVersion(file))
    112 					allFiles.emplace_back(std::move(file));
    113 			}
    114 		}
    115 
    116 		if(allFiles.empty())
    117 			return Rom::invalid();
    118 
    119 		std::map<FileType, std::vector<File>> romsByType;
    120 		for (auto& file : allFiles)
    121 		{
    122 			romsByType[file.type].push_back(file);
    123 		}
    124 
    125 		auto& fullRoms = romsByType[FileType::FullRom];
    126 
    127 		if(fullRoms.empty())
    128 			return Rom::invalid();
    129 
    130 		File best;
    131 
    132 		// use the highest version that we have
    133 		for (auto& fullRom : fullRoms)
    134 		{
    135 			if(fullRom.version > best.version)
    136 				best = std::move(fullRom);
    137 		}
    138 
    139 		// apply OS update if we have any and the version of that upgrade is higher
    140 		auto& midiUpgrades = romsByType[FileType::MidiUpdate];
    141 
    142 		File bestMidi;
    143 		for (auto& midi : midiUpgrades)
    144 		{
    145 			if(midi.version > bestMidi.version && midi.version > best.version)
    146 				bestMidi = std::move(midi);
    147 		}
    148 
    149 		if(bestMidi.version)
    150 		{
    151 			assert(bestMidi.data.size() <= best.data.size());
    152 			::memcpy(best.data.data(), bestMidi.data.data(), bestMidi.data.size());
    153 			best.version = bestMidi.version;
    154 			best.name += "_upgraded_" + bestMidi.name;
    155 		}
    156 
    157 		return {best.name, best.data};
    158 	}
    159 
    160 	std::vector<RomLoader::File> RomLoader::findFiles(const std::string& _extension, const size_t _sizeMin, const size_t _sizeMax)
    161 	{
    162 		const auto fileNames = synthLib::RomLoader::findFiles(_extension, _sizeMin, _sizeMax);
    163 
    164 		std::vector<File> files;
    165 
    166 		for (const auto& name : fileNames)
    167 		{
    168 			File f;
    169 			if(!baseLib::filesystem::readFile(f.data, name))
    170 				continue;
    171 
    172 			f.name = baseLib::filesystem::getFilenameWithoutPath(name);
    173 			files.emplace_back(std::move(f));
    174 		}
    175 		return files;
    176 	}
    177 
    178 	bool RomLoader::detectFileType(File& _file)
    179 	{
    180 		const auto& data = _file.data;
    181 
    182 		switch (data.size())
    183 		{
    184 		case g_romSizeFull:
    185 			// full rom starts with C0DE
    186 			if(data[0] != 0xc0 || data[1] != 0xde)
    187 				return false;
    188 			_file.type = FileType::FullRom;
    189 			return true;
    190 		case g_romSizeHalf:
    191 			// rom half starts with either C0 or DE
    192 			if(data[0] == 0xc0 && data[1] == 0x00)
    193 				_file.type = FileType::HalfRomA;
    194 			else if(data[0] == 0xde && data[1] == 0x00)
    195 				_file.type = FileType::HalfRomB;
    196 			else
    197 				return false;
    198 			return true;
    199 		default:
    200 			// OS update starts with C0DE too
    201 			if(data[0] != 0xc0 || data[1] != 0xde)
    202 				return false;
    203 			_file.type = FileType::MidiUpdate;
    204 			return true;
    205 		}
    206 	}
    207 
    208 	bool RomLoader::removeBootloader(std::vector<uint8_t>& _data)
    209 	{
    210 		if(_data.size() < 2)
    211 			return false;
    212 
    213 		const std::vector<uint8_t> pattern{0xc0, 0xde, 0x00, 0x00};
    214 
    215 		const auto it = std::search(_data.begin(), _data.end(), pattern.begin(), pattern.end());
    216 
    217 		if(it == _data.end())
    218 			return false;
    219 
    220 		if(it != _data.begin())
    221 			_data.erase(_data.begin(), it);
    222 
    223 		return true;
    224 	}
    225 
    226 	bool RomLoader::detectVersion(File& _file)
    227 	{
    228 		_file.version = 0;
    229 
    230 		const auto& data = _file.data;
    231 		if(data.size() < 0x33)
    232 			return false;
    233 		if(data[0x30] != '.')
    234 			return false;
    235 
    236 		_file.version = (data[0x2f] - '0') * 100 + (data[0x31] - '0') * 10 + (data[0x32] - '0');
    237 
    238 		return _file.version > 200;
    239 	}
    240 }