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 }