18#include <radio_tool/radio/radio_factory.hpp>
19#include <radio_tool/fw/fw_factory.hpp>
20#include <radio_tool/codeplug/codeplug_factory.hpp>
22#include <radio_tool/radio/tyt_radio.hpp>
23#include <radio_tool/dfu/dfu_exception.hpp>
24#include <radio_tool/util.hpp>
25#include <radio_tool/version.hpp>
28#include <xor_tool.hpp>
41auto GetOptionOrErr(
const cxxopts::ParseResult &cmd,
const std::string &v,
const std::string &err) ->
const T &
45 return cmd[v].as<T>();
49 throw std::runtime_error(err);
53int main(
int argc,
char **argv)
57 std::stringstream ssVersion;
58 ssVersion << g_PROJECT_NAME <<
" v" << g_PROJECT_VERSION <<
"-" << g_GIT_SHA1_SHORT;
59 auto version = ssVersion.str();
61 cxxopts::Options options(argv[0], version);
64 options.add_options(
"General")
65 (
"h,help",
"Show this message", cxxopts::value<std::string>()->implicit_value(
""),
"<command>")
66 (
"l,list",
"List devices")
67 (
"d,device",
"Device to use", cxxopts::value<uint16_t>(),
"<index>")
68 (
"i,in",
"Input file", cxxopts::value<std::string>(),
"<file>")
69 (
"o,out",
"Output file", cxxopts::value<std::string>(),
"<file>");
71 options.add_options(
"Programming")
72 (
"f,flash",
"Flash firmware")
73 (
"p,program",
"Upload codeplug");
75 options.add_options(
"All radio")
76 (
"info",
"Print some info about the radio")
77 (
"get-status",
"Print the current DFU Status");
79 options.add_options(
"TYT Radio")
80 (
"get-time",
"Gets the radio time")
81 (
"set-time",
"Sets the radio time")
82 (
"dump-reg",
"Dump a register from the radio", cxxopts::value<uint16_t>(),
"<register>")
83 (
"reboot",
"Reboot the radio")
84 (
"dump-bootloader",
"Dump bootloader (Mac only)");
86 options.add_options(
"Firmware")
87 (
"fw-info",
"Print info about a firmware file")
88 (
"wrap",
"Wrap a firmware bin (use --help wrap, for more info)")
89 (
"unwrap",
"Unwrap a fimrware file")
91 (
"make-xor",
"Try to make an XOR key for the input firmware");
96 options.add_options(
"Codeplug")
97 (
"codeplug-info",
"Print info about a codeplug file");
99 options.add_options(
"Wrap")
100 (
"s,segment",
"Add a segment for wrapping", cxxopts::value<std::vector<std::string>>(),
"<0x08000000:region_0.bin>")
101 (
"r,radio",
"Radio to build firmware file for", cxxopts::value<std::string>(),
"<DM1701>");
103 auto cmd = options.parse(argc, argv);
106 if (cmd.count(
"help") || cmd.arguments().empty())
108 std::vector<std::string> help_groups;
110 auto section = cmd.count(
"help") ? cmd[
"help"].as<std::string>() : std::string();
111 if (!section.empty())
113 for (
const auto &g : options.groups())
115 if (g.size() != section.size())
117 if (std::equal(g.begin(), g.end(), section.begin(), [](
char a,
char b)
118 { return std::toupper(a) == std::toupper(b); }))
120 help_groups.push_back(g);
126 help_groups = {
"General",
"Programming",
"Firmware",
"All radio",
"TYT Radio",
"Codeplug"};
128 std::cerr << options.help(help_groups) << std::endl;
132 if (cmd.count(
"list-radios"))
139 if (cmd.count(
"fw-info"))
141 auto file = GetOptionOrErr<std::string>(cmd,
"in",
"Input file not specified");
145 std::cerr << fw->ToString();
149 if (cmd.count(
"codeplug-info"))
151 auto file = GetOptionOrErr<std::string>(cmd,
"in",
"Input file not specified");
153 auto h = CodeplugFactory::GetCodeplugHandler(file);
155 std::cerr << h->ToString();
159 if (cmd.count(
"wrap"))
161 auto out = GetOptionOrErr<std::string>(cmd,
"out",
"Output file not specified");
162 auto radio = GetOptionOrErr<std::string>(cmd,
"radio",
"Radio not specified");
163 auto segments = GetOptionOrErr<std::vector<std::string>>(cmd,
"segment",
"Must specify at least 1 segment");
166 fw->SetRadioModel(radio);
167 for (
const auto &sx : segments)
169 auto schar = sx.find(
':');
170 if (schar != sx.npos)
173 auto addr_str = sx.substr(0, schar);
174 if (addr_str.find(
"0x") != addr_str.npos)
176 addr = std::stoi(addr_str, 0, 16);
180 addr = std::stoi(addr_str, 0, 10);
183 auto filename = sx.substr(schar + 1);
184 std::cerr <<
"Adding segment 0x"
185 << std::hex << std::setw(8) << std::setfill(
'0') << addr
186 <<
" from file " << filename << std::endl;
188 std::ifstream f_seg(filename, std::ios_base::binary);
191 f_seg.seekg(0, f_seg.end);
192 auto len = f_seg.tellg();
193 f_seg.seekg(0, f_seg.beg);
195 std::vector<uint8_t> seg_data;
196 seg_data.resize(len);
197 f_seg.read((
char *)seg_data.data(), len);
200 fw->AppendSegment(addr, seg_data);
204 throw std::runtime_error(
"Cant open file for segment");
209 throw std::invalid_argument(
"Segments must be in the format '0x0000:filename.bin'");
215 std::cerr <<
"Done!" << std::endl;
219 if (cmd.count(
"unwrap"))
221 auto in_file = GetOptionOrErr<std::string>(cmd,
"in",
"Input file not specified");
222 auto out_file = GetOptionOrErr<std::string>(cmd,
"out",
"Output file not specified");
225 fw_handler->Read(in_file);
226 fw_handler->Decrypt();
228 for (
const auto &rn : fw_handler->GetDataSegments())
230 std::stringstream ss_name;
231 ss_name << out_file <<
"_0x" << std::setw(8) << std::setfill(
'0') << std::hex << rn.address;
233 std::ofstream fw_out;
234 fw_out.open(ss_name.str(), std::ios_base::out | std::ios_base::binary);
235 if (fw_out.is_open())
237 fw_out.write((
const char *)rn.data.data(), rn.data.size());
242 std::cerr <<
"Failed to open output file: " << out_file << std::endl;
250 if (cmd.count(
"make-xor"))
252 auto in_file = GetOptionOrErr<std::string>(cmd,
"in",
"Input file not specified");
254 fw_handler->Read(in_file);
256 auto key = radio_tool::fw::XORTool::MakeXOR(fw_handler->GetData());
257 for (
const auto ®ion : fw_handler->GetDataSegments())
259 if (radio_tool::fw::XORTool::Verify(region.address, region.data, key))
262 <<
"Region @ 0x" << std::setfill(
'0') << std::setw(8) << std::hex << region.address
263 <<
" appears to be a valid vector_table" << std::endl;
267 radio_tool::PrintHex(key.begin(), key.end());
270 std::cout <<
"const uint8_t[] key = {";
271 for(
const auto &v : key) {
272 std::cout <<
"0x" << std::hex << std::setfill(
'0') << std::setw(2) << (int)v <<
",";
274 std::cout <<
"};" << std::endl;
280 if (cmd.count(
"list"))
282 for (
const auto &d : rdFactory.ListDevices())
284 std::wcout << d->ToString() << std::endl;
289 if (!cmd.count(
"device"))
291 std::cout <<
"You must select a device" << std::endl;
295 auto index = cmd[
"device"].as<uint16_t>();
296 auto radio = rdFactory.OpenDevice(index);
298 if (cmd.count(
"info"))
300 std::cout << radio->ToString() << std::endl;
304 if (cmd.count(
"flash"))
306 auto in_file = GetOptionOrErr<std::string>(cmd,
"in",
"Input file not specified");
307 radio->WriteFirmware(in_file);
308 std::cout <<
"Done!" << std::endl;
312 if (cmd.count(
"program"))
318 std::cerr <<
"DFU Error: " << dfuEx.what() << std::endl;
321 catch (
const cxxopts::OptionException &e)
323 std::cerr <<
"error parsing options: " << e.what() << std::endl;
326 catch (
const std::exception &gex)
328 std::cerr <<
"Error: " << gex.what() << std::endl;
333auto tytCommands(
const cxxopts::ParseResult &cmd,
RadioOperations *radio) ->
void
337 std::cerr <<
"Cant use TYT commands on non-tyt radio!" << std::endl;
342 auto dfu = tyt_radio->
GetDFU();
344 if (cmd.count(
"get-status"))
346 auto status = dfu->GetStatus();
347 std::cerr << status.ToString() << std::endl;
350 if (cmd.count(
"dump-reg"))
352 auto x = cmd[
"dump-reg"].as<uint16_t>();
353 std::cerr <<
"Read register: 0x" << std::setfill(
'0') << std::setw(2) << std::hex << x << std::endl;
357 if (cmd.count(
"dump-bootloader"))
359 auto out_file = GetOptionOrErr<std::string>(cmd,
"out",
"Input file not specified");
362 outf.open(out_file, std::ios_base::out | std::ios_base::binary);
365 auto mem = dfu->Upload(size, 2);
367 outf.write((
char *)mem.data(), mem.size());
372 std::cerr <<
"Failed to open output file: " << out_file << std::endl;
377 if (cmd.count(
"get-time"))
383 if (cmd.count(
"set-time"))
388 if (cmd.count(
"reboot"))