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

unpacker.cpp (4718B)


      1 #include "unpacker.h"
      2 
      3 #include "utils.h"
      4 
      5 #include "dsp56kEmu/logging.h"
      6 
      7 #include <vector>
      8 
      9 namespace virusLib
     10 {
     11 	bool ROMUnpacker::isValidInstaller(std::istream& _file)
     12 	{
     13 		_file.seekg(0);
     14 		char magic[5] = {0};
     15 		_file.read(magic, 4);
     16 		_file.seekg(-4, std::ios_base::cur);  // restore cursor
     17 		return std::string(magic) == "FORM";
     18 	}
     19 
     20 	ROMUnpacker::Firmware ROMUnpacker::getFirmware(std::istream& _file, const DeviceModel _model)
     21 	{
     22 		// First round of extraction
     23 		// This will yield the installer files (html, lcd firmware, vti firmware, etc)
     24 		std::vector<Chunk> chunks = getChunks(_file);
     25 
     26 		// The first chunk should be the _file table containing filenames
     27 		Chunk table = chunks[0];
     28 		if (std::string(table.name) != "TABL")
     29 		{
     30 			LOG("Installer file table not found");
     31 			return Firmware{};
     32 		}
     33 
     34 		// Read all the filenames (starting at offset 1)
     35 		std::vector<std::string> filenames{};
     36 		std::string currentFilename;
     37 		for (auto it = begin(table.data) + 1; it != end(table.data); ++it)
     38 		{
     39 			if (*it == '\0')
     40 			{
     41 				filenames.emplace_back(currentFilename);
     42 				currentFilename.clear();
     43 			} else
     44 			{
     45 				currentFilename += *it;
     46 			}
     47 		}
     48 
     49 		uint8_t tableItemCount = table.data[0];
     50 		assert (filenames.size() == tableItemCount);
     51 		assert (chunks.size() - 1 == tableItemCount);
     52 
     53 		// Find the VTI _file for the given _model
     54 		const std::string vtiFilename = getVtiFilename(_model);
     55 		Chunk* vti = nullptr;
     56 		for (auto& f: filenames)
     57 		{
     58 			// Find the matching chunk for this filename
     59 			auto i = &f - &filenames[0] + 1;
     60 			LOG("Chunk " << chunks[i].name << " = " << f << " (size=0x" << HEX(chunks[i].size) << ")");
     61 			if (f == vtiFilename)
     62 			{
     63 				vti = &chunks[i];
     64 			}
     65 		}
     66 
     67 		if (!vti)
     68 		{
     69 			LOG("Could not find the VTI _file");
     70 			return Firmware{};
     71 		}
     72 
     73 		// Second round of extraction
     74 		// This will yield the packed chunks of the VTI _file containing F.bin, S.bin, P.bin
     75 		LOG("Found VTI: " << vtiFilename << " in chunk " << vti->name << ", size=0x" << HEX(vti->data.size()));
     76 		imemstream stream(vti->data);
     77 
     78 		std::vector<Chunk> parts = getChunks(stream);
     79 		if (parts.empty())
     80 		{
     81 			return Firmware{};
     82 		}
     83 
     84 		// Perform unpacking for all roms
     85 		std::vector<char> dsp = unpackFile(parts, 'F');
     86 		std::vector<std::vector<char>> presets{};
     87 		for (const char& presetFileId: {'P', 'S'})
     88 		{
     89 			auto presetFile = unpackFile(parts, presetFileId);
     90 			if (!presetFile.empty())
     91 			{
     92 				presets.emplace_back(std::move(presetFile));
     93 			}
     94 		}
     95 
     96 		return Firmware{std::move(dsp), std::move(presets)};
     97 	}
     98 
     99 	std::string ROMUnpacker::getVtiFilename(const DeviceModel _model)
    100 	{
    101 		switch (_model)
    102 		{
    103 		case DeviceModel::TI:
    104 			return "vti.bin";
    105 		case DeviceModel::TI2:
    106 			return "vti_2.bin";
    107 		case DeviceModel::Snow:
    108 			return "vti_snow.bin";
    109 		default:
    110 			return {};
    111 		}
    112 	}
    113 
    114 	std::vector<ROMUnpacker::Chunk> ROMUnpacker::getChunks(std::istream& _file)
    115 	{
    116 		std::vector<Chunk> result;
    117 		char filename[5] = {0};
    118 		uint32_t filesize;
    119 		_file.read(filename, 4);
    120 		_file.read(reinterpret_cast<char*>(&filesize), 4);
    121 		filesize = swap32(filesize);
    122 
    123 		LOG("FileID: " << filename);
    124 		LOG("FileSize: " << filesize);
    125 
    126 		while (!_file.eof())
    127 		{
    128 			Chunk chunk{};
    129 			_file.read(chunk.name, 4);
    130 			_file.read(reinterpret_cast<char*>(&chunk.size), 4);
    131 			chunk.size = swap32(chunk.size);
    132 			if (chunk.size > 0)
    133 			{
    134 				chunk.data.resize(chunk.size);
    135 				_file.read(chunk.data.data(), chunk.size);
    136 				result.emplace_back(std::move(chunk));
    137 			}
    138 		}
    139 
    140 		return result;
    141 	}
    142 
    143 	std::vector<char> ROMUnpacker::unpackFile(const std::vector<Chunk>& _chunks, const char _fileId)
    144 	{
    145 		std::vector<char> content;
    146 		content.reserve(1 << 20);
    147 
    148 		for (const auto& chunk : _chunks)
    149 		{
    150 			if (chunk.name[0] != _fileId)
    151 			{
    152 				continue;
    153 			}
    154 
    155 			if(chunk.data.size() % 35 == 2)
    156 			{
    157 				size_t ctr = 0;
    158 
    159 				// 4 chunk ID, 4 chunk length, 1 block id?, 2 segment offset in bytes (32)
    160 				// each chunk has 2 remaining bytes (checksum?)
    161 				for (size_t i = 0; i < chunk.size - 2; i += 35)
    162 				{
    163 					const auto idx = swap16(*reinterpret_cast<const uint16_t*>(&chunk.data[i + 1]));
    164 					assert (idx == ctr);
    165 					for (size_t j = 0; j < 32; ++j)
    166 					{
    167 						content.emplace_back(chunk.data[i + j + 3]);
    168 					}
    169 					ctr += 32;
    170 				}
    171 			}
    172 			else
    173 			{
    174 				// slightly different chunk format
    175 				// 4 chunk ID, 4 chunk length, 1 chunk id, 1 counter per segment, 1 block id?
    176 				assert(chunk.data.size() % 35 == 0);
    177 
    178 				uint8_t ctr = 0;
    179 				for (size_t i = 0; i < chunk.size - 2; i += 35)
    180 				{
    181 					const auto idx = static_cast<uint8_t>(chunk.data[i + 1]);
    182 
    183 					assert (idx == ctr);
    184 					for (size_t j = 0; j < 32; ++j)
    185 					{
    186 						content.emplace_back(chunk.data[i + j + 3]);
    187 					}
    188 					++ctr;
    189 				}
    190 			}
    191 		}
    192 
    193 		return content;
    194 	}
    195 }