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

romfile.cpp (8935B)


      1 #include <fstream>
      2 #include <algorithm>
      3 
      4 #include "romfile.h"
      5 #include "utils.h"
      6 
      7 #include "unpacker.h"
      8 
      9 #include "dsp56kEmu/dsp.h"
     10 #include "dsp56kEmu/logging.h"
     11 
     12 #include <cstring>	// memcpy
     13 
     14 #include "demoplaybackTI.h"
     15 #include "dspSingle.h"
     16 
     17 namespace virusLib
     18 {
     19 
     20 ROMFile::ROMFile(std::vector<uint8_t> _data, std::string _name, const DeviceModel _model/* = DeviceModel::ABC*/) : m_model(_model), m_romFileName(std::move(_name)), m_romFileData(std::move(_data))
     21 {
     22 	if(initialize())
     23 		return;
     24 	m_romFileData.clear();
     25 	m_bootRom.size = 0;
     26 }
     27 
     28 ROMFile ROMFile::invalid()
     29 {
     30 	return ROMFile({}, {}, DeviceModel::Invalid);
     31 }
     32 
     33 bool ROMFile::initialize()
     34 {
     35 	std::unique_ptr<std::istream> dsp(new imemstream(reinterpret_cast<std::vector<char>&>(m_romFileData)));
     36 
     37 	ROMUnpacker::Firmware fw;
     38 
     39 	// Check if we are dealing with a TI installer file, if so, unpack it first
     40 	if (ROMUnpacker::isValidInstaller(*dsp))
     41 	{
     42 		fw = ROMUnpacker::getFirmware(*dsp, m_model);
     43 		if (!fw.isValid())
     44 		{
     45 			LOG("Could not unpack ROM file");
     46 			return false;
     47 		}
     48 
     49 		// Wrap into a stream so we can pass it into readChunks
     50 		dsp.reset(new imemstream(fw.DSP));
     51 	}
     52 
     53 	const auto chunks = readChunks(*dsp);
     54 
     55 	if (chunks.empty())
     56 		return false;
     57 
     58 	m_bootRom.size = chunks[0].items[0];
     59 	m_bootRom.offset = chunks[0].items[1];
     60 	m_bootRom.data = std::vector<uint32_t>(m_bootRom.size);
     61 
     62 	// The first chunk contains the bootrom
     63 	uint32_t i = 2;
     64 	for (; i < m_bootRom.size + 2; i++)
     65 	{
     66 		m_bootRom.data[i-2] = chunks[0].items[i];
     67 	}
     68 
     69 	// The rest of the chunks is made up of the command stream
     70 	for (size_t j = 0; j < chunks.size(); j++)
     71 	{
     72 		for (; i < chunks[j].items.size(); i++)
     73 			m_commandStream.emplace_back(chunks[j].items[i]);
     74 		i = 0;
     75 	}
     76 
     77 	printf("Program BootROM size = 0x%x\n", m_bootRom.size);
     78 	printf("Program BootROM offset = 0x%x\n", m_bootRom.offset);
     79 	printf("Program CommandStream size = 0x%x\n", static_cast<uint32_t>(m_commandStream.size()));
     80 
     81 	if(isTIFamily())
     82 	{
     83 		if (!fw.Presets.empty())
     84 		{
     85 			for (const auto & preset : fw.Presets)
     86 			{
     87 				m_demoData.insert(m_demoData.begin(), preset.begin(), preset.end());
     88 				if(DemoPlaybackTI::findDemoData(m_demoData))
     89 					break;
     90 
     91 				m_demoData.clear();
     92 			}
     93 		}
     94 		else
     95 		{
     96 			loadPresetFiles();
     97 		}
     98 
     99 		// The Snow even has multis, but they are not sequencer compatible, drop them
    100 		m_multis.clear();
    101 
    102 		if(m_multis.empty())
    103 		{
    104 			// there is no multi in the TI presets, but there is an init multi in the F.bin
    105 
    106 			const std::string search = "Init Multi";
    107 			const auto searchSize = search.size();
    108 
    109 			for(size_t i=0; i<fw.DSP.size() && m_multis.empty(); ++i)
    110 			{
    111 				for(size_t j=0; j<searchSize && m_multis.empty(); ++j)
    112 				{
    113 					if(fw.DSP[i+j] != search[j])
    114 						break;
    115 
    116 					if(j == searchSize-1)
    117 					{
    118 						TPreset preset;
    119 						memcpy(preset.data(), &fw.DSP[i - 4], std::size(preset));
    120 
    121 						// validate that we found the correct data by checking part volumes. It might just be a string somewhere in the data
    122 						for(size_t k=0; k<16; ++k)
    123 						{
    124 							if(preset[k + 0x8b] != 0x40)
    125 								break;
    126 
    127 							if(k == 15)
    128 							{
    129 								for(size_t p=0; p<getPresetsPerBank(); ++p)
    130 									m_multis.push_back(preset);
    131 							}
    132 						}
    133 					}
    134 				}
    135 			}
    136 		}
    137 
    138 		//load presets in a fixed order, TI first, Snow last
    139 		auto loadFirmwarePresets = [this](const DeviceModel _model)
    140 		{
    141 			const std::unique_ptr<imemstream> file(new imemstream(reinterpret_cast<std::vector<char>&>(m_romFileData)));
    142 			const auto firmware = ROMUnpacker::getFirmware(*file, _model);
    143 			if(!firmware.Presets.empty())
    144 			{
    145 				for (auto& presetFile: firmware.Presets)
    146 				{
    147 					imemstream stream(presetFile);
    148 					loadPresetFile(stream, _model);
    149 				}
    150 			}
    151 		};
    152 
    153 		loadFirmwarePresets(DeviceModel::TI);
    154 		loadFirmwarePresets(DeviceModel::TI2);
    155 		loadFirmwarePresets(DeviceModel::Snow);
    156 	}
    157 
    158 	return true;
    159 }
    160 
    161 uint32_t ROMFile::getRomBankCount(const DeviceModel _model)
    162 {
    163 	switch (_model)
    164 	{
    165 	case DeviceModel::TI:
    166 		return 26 - 7;
    167 	case DeviceModel::Snow:
    168 		return 8;
    169 	case DeviceModel::TI2:
    170 		return 26 - 5 + 4;
    171 	default:
    172 		return 8;
    173 	}
    174 }
    175 
    176 std::vector<ROMFile::Chunk> ROMFile::readChunks(std::istream& _file) const
    177 {
    178 	_file.seekg(0, std::ios_base::end);
    179 	const auto fileSize = _file.tellg();
    180 
    181 	uint32_t offset;
    182 	int lastChunkId;
    183 
    184 	if(fileSize == getRomSizeModelD())
    185 	{
    186 		assert(isTIFamily());
    187 		offset = 0x70000;
    188 		lastChunkId = 14;
    189 	}
    190 	else if (fileSize == getRomSizeModelABC() || fileSize == getRomSizeModelABC()/2)	// the latter is a ROM without presets
    191 	{
    192 		// ABC
    193 		assert(isABCFamily(m_model));
    194 		offset = 0x18000;
    195 		lastChunkId = 4;
    196 	}
    197 	else 
    198 	{
    199 		LOG("Invalid ROM, unexpected filesize");
    200 		return {};
    201 	}
    202 
    203 	std::vector<Chunk> chunks;
    204 	chunks.reserve(lastChunkId + 1);
    205 
    206 	// Read all the chunks
    207 	for (int i = 0; i <= lastChunkId; i++)
    208 	{
    209 		_file.seekg(offset);
    210 
    211 		// Read buffer
    212 		Chunk chunk;
    213 		//file.read(reinterpret_cast<char *>(&chunk->chunk_id), 1);
    214 		_file.read(reinterpret_cast<char*>(&chunk.chunk_id), 1);
    215 		_file.read(reinterpret_cast<char*>(&chunk.size1), 1);
    216 		_file.read(reinterpret_cast<char*>(&chunk.size2), 1);
    217 
    218 		if(i == 0 && chunk.chunk_id == 3 && lastChunkId == 4)	// Virus A and old Virus B OSs have one chunk less
    219 			lastChunkId = 3;
    220 
    221 		if(chunk.chunk_id != lastChunkId - i)
    222 			return {};
    223 
    224 		// Format uses a special kind of size where the first byte should be decreased by 1
    225 		const uint16_t len = ((chunk.size1 - 1) << 8) | chunk.size2;
    226 
    227 		for (uint32_t j = 0; j < len; j++)
    228 		{
    229 			uint8_t buf[3];
    230 			_file.read(reinterpret_cast<char*>(buf), 3);
    231 			chunk.items.emplace_back((buf[0] << 16) | (buf[1] << 8) | buf[2]);
    232 		}
    233 
    234 		chunks.emplace_back(chunk);
    235 
    236 		offset += 0x8000;
    237 	}
    238 
    239 	return chunks;
    240 }
    241 
    242 bool ROMFile::loadPresetFiles()
    243 {
    244 	bool res = true;
    245 	for (auto &filename: {"S.bin", "P.bin"})
    246 	{
    247 		std::ifstream file(filename, std::ios::binary | std::ios::ate);
    248 		if (!file.is_open())
    249 		{
    250 			LOG("Failed to open preset file " << filename);
    251 			res = false;
    252 			continue;
    253 		}
    254 		res &= loadPresetFile(file, m_model);
    255 		file.close();
    256 	}
    257 	return res;
    258 }
    259 
    260 bool ROMFile::loadPresetFile(std::istream& _file, DeviceModel _model)
    261 {
    262 	_file.seekg(0, std::ios_base::end);
    263 	const auto fileSize = _file.tellg();
    264 
    265 	uint32_t singleCount = 0;
    266 	uint32_t multiCount = 0;
    267 	uint32_t multiOffset = 0;
    268 
    269 	if (fileSize == 0x1b0000)		// TI/TI2
    270 	{
    271 		if(_model == DeviceModel::TI2)
    272 			singleCount = 128 * (26 - 5);
    273 		else
    274 			singleCount = 128 * (26 - 7);
    275 	}
    276 	else if (fileSize == 0x40000)	// Snow A
    277 	{
    278 		singleCount = 512;
    279 	}
    280 	else if (fileSize == 0x68000)	// Snow B
    281 	{
    282 		singleCount = 512;
    283 		multiCount = 128;
    284 		multiOffset = 768;
    285 	}
    286 	else
    287 	{
    288 		LOG("Unknown file size " << fileSize << " for preset file");
    289 		return false;
    290 	}
    291 
    292 	_file.seekg(0);
    293 
    294 	for(uint32_t i=0; i<singleCount; ++i)
    295 	{
    296 		TPreset single;
    297 		_file.read(reinterpret_cast<char*>(&single), sizeof(single));
    298 		m_singles.emplace_back(single);
    299 
    300 #ifdef _DEBUG
    301 		LOG("Loaded single " << i << ", name = " << getSingleName(single));
    302 #endif
    303 	}
    304 
    305 	if(multiCount)
    306 	{
    307 		const auto off = std::max(singleCount, multiOffset);
    308 		_file.seekg(off * 512);
    309 
    310 		for (uint32_t i = 0; i < multiCount; ++i)
    311 		{
    312 			TPreset multi;
    313 			_file.read(reinterpret_cast<char*>(&multi), sizeof(multi));
    314 			m_multis.emplace_back(multi);
    315 
    316 #ifdef _DEBUG
    317 			LOG("Loaded multi " << i << ", name = " << getMultiName(multi));
    318 #endif
    319 		}
    320 	}
    321 
    322 	return true;
    323 }
    324 
    325 std::thread ROMFile::bootDSP(DspSingle& _dsp) const
    326 {
    327 	return _dsp.boot(m_bootRom, m_commandStream);
    328 }
    329 
    330 std::string ROMFile::getModelName() const
    331 {
    332 	return virusLib::getModelName(getModel());
    333 }
    334 
    335 bool ROMFile::getSingle(const int _bank, const int _presetNumber, TPreset& _out) const
    336 {
    337 	if(isTIFamily())
    338 	{
    339 		const auto offset = _bank * getSinglesPerBank() + _presetNumber;
    340 		if (offset >= m_singles.size())
    341 			return false;
    342 		_out = m_singles[offset];
    343 		return true;
    344 	}
    345 
    346 	const uint32_t offset = 0x50000 + (_bank * 0x8000) + (_presetNumber * getSinglePresetSize());
    347 
    348 	return getPreset(offset, _out);
    349 }
    350 
    351 bool ROMFile::getMulti(const int _presetNumber, TPreset& _out) const
    352 {
    353 	if(isTIFamily())
    354 	{
    355 		if (_presetNumber >= m_multis.size())
    356 			return false;
    357 
    358 		_out = m_multis[_presetNumber];
    359 		return true;
    360 	}
    361 
    362 	return getPreset(0x48000 + (_presetNumber * getMultiPresetSize()), _out);
    363 }
    364 
    365 bool ROMFile::getPreset(const uint32_t _offset, TPreset& _out) const
    366 {
    367 	if(_offset + getSinglePresetSize() > m_romFileData.size())
    368 		return false;
    369 
    370 	memcpy(_out.data(), &m_romFileData[_offset], getSinglePresetSize());
    371 	return true;
    372 }
    373 
    374 std::string ROMFile::getSingleName(const TPreset& _preset)
    375 {
    376 	return getPresetName(_preset, 240, 249);
    377 }
    378 
    379 std::string ROMFile::getMultiName(const TPreset& _preset)
    380 {
    381 	return getPresetName(_preset, 4, 13);
    382 }
    383 
    384 std::string ROMFile::getPresetName(const TPreset& _preset, const uint32_t _first, const uint32_t _last)
    385 {
    386 	std::string name;
    387 
    388 	name.reserve(11);
    389 
    390 	for (uint32_t i = _first; i <= _last; i++)
    391 	{
    392 		const auto c = _preset[i];
    393 		if(c < 32 || c > 127)
    394 			break;
    395 		name.push_back(static_cast<char>(c));
    396 	}
    397 
    398 	return name;
    399 }
    400 
    401 }