You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
292 lines
12 KiB
C++
292 lines
12 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2007 Nicolas Hadacek <hadacek@kde.org> *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
***************************************************************************/
|
|
#include "main.h"
|
|
|
|
#include "common/global/pfile.h"
|
|
#include "common/cli/cli_log.h"
|
|
#include "devices/list/device_list.h"
|
|
#include "devices/pic/base/pic.h"
|
|
#include "devices/pic/pic/pic_memory.h"
|
|
#include "devices/base/device_group.h"
|
|
#include "common/global/about.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
const TDECmdLineOptions OPTIONS[] = {
|
|
TDECmdLineLastOption
|
|
};
|
|
|
|
const CLI::CommandData CLI::NORMAL_COMMAND_DATA[] = {
|
|
{ "check", NeedSource1 | NeedCorrectInput, I18N_NOOP("Check hex file for correctness (if a device is specified, check if hex file is compatible with it).") },
|
|
{ "info", NeedSource1 | NeedCorrectInput, I18N_NOOP("Return information about hex file.") },
|
|
{ "fix", NeedSource1 | NeedDestination, I18N_NOOP("Clean hex file and fix errors (wrong CRC, truncated line, truncated file).") },
|
|
{ "compare", NeedSource1 | NeedSource2 | NeedCorrectInput, I18N_NOOP("Compare two hex files.") },
|
|
{ "checksum", NeedSource1 | NeedCorrectInput | NeedDevice, I18N_NOOP("Return checksum.") },
|
|
{ "create", NeedDestination | NeedDevice, I18N_NOOP("Create an hex file for the specified device.") },
|
|
{ 0, NoCommandProperty, 0 }
|
|
};
|
|
const CLI::CommandData CLI::INTERACTIVE_COMMAND_DATA[] = {
|
|
{ 0, NoCommandProperty, 0 }
|
|
};
|
|
|
|
const CLI::PropertyData CLI::PROPERTY_DATA[] = {
|
|
{ "device", "device <name>", "d", I18N_NOOP("Target device."), "device-list", I18N_NOOP("Return the list of supported devices.") },
|
|
{ "fill", "fill <value>", 0, I18N_NOOP("Fill option."), "fill-list", I18N_NOOP("Return the list of supported fill options.") },
|
|
{ 0, 0, 0, 0, 0, 0 }
|
|
};
|
|
|
|
const TDECmdLineOptions CLI::OPTIONS[] = {
|
|
TDECmdLineLastOption
|
|
};
|
|
|
|
const CLI::FillOptions CLI::FILL_OPTIONS[] = {
|
|
{ "blank", I18N_NOOP("Fill with blank values (default).") },
|
|
{ "zero", I18N_NOOP("Fill with zeroes.") },
|
|
{ "checksum_check", I18N_NOOP("Fill for checksum verification (cf datasheets).") },
|
|
{ 0, 0 }
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
CLI::Main::Main()
|
|
: MainBase(HasForce), _device(0), _memory(0)
|
|
{}
|
|
|
|
CLI::Main::~Main()
|
|
{
|
|
delete _memory;
|
|
}
|
|
|
|
CLI::ExitCode CLI::Main::prepareCommand(const TQString &command)
|
|
{
|
|
const CommandData *data = findCommandData(command);
|
|
CommandProperties properties = static_cast<CommandProperties>(data->properties);
|
|
int nbArgs = 0;
|
|
if ( properties & NeedSource1 ) nbArgs++;
|
|
if ( properties & NeedSource2 ) nbArgs++;
|
|
if ( properties & NeedDestination ) nbArgs++;
|
|
if ( _args->count()<nbArgs ) return errorExit(i18n("Too few arguments."), ARG_ERROR);
|
|
if ( _args->count()>nbArgs ) return errorExit(i18n("Too many arguments."), ARG_ERROR);
|
|
uint argIndex = 0;
|
|
if ( properties & NeedSource1 ) {
|
|
PURL::Url url = PURL::Url(_args->url(argIndex));
|
|
argIndex++;
|
|
PURL::File file(url, *_view);
|
|
if ( !file.openForRead() ) return FILE_ERROR;
|
|
_errors = _source1.load(file.stream(), _format);
|
|
if ( (properties & NeedCorrectInput) && !_errors.isEmpty() ) {
|
|
TQString s = (properties & NeedSource2 ? i18n("First hex file: ") : TQString());
|
|
for (uint i=0; i<uint(_errors.count()); i++) log(Log::LineType::Error, s + _errors[i].message());
|
|
return EXEC_ERROR;
|
|
}
|
|
}
|
|
if ( properties & NeedSource2 ) {
|
|
PURL::Url url = PURL::Url(_args->url(argIndex));
|
|
argIndex++;
|
|
PURL::File file(url, *_view);
|
|
if ( !file.openForRead() ) return FILE_ERROR;
|
|
_errors = _source2.load(file.stream(), _format);
|
|
if ( (properties & NeedCorrectInput) && !_errors.isEmpty() ) {
|
|
TQString s = (properties & NeedSource1 ? i18n("Second hex file: ") : TQString());
|
|
for (uint i=0; i<uint(_errors.count()); i++) log(Log::LineType::Error, s + _errors[i].message());
|
|
return EXEC_ERROR;
|
|
}
|
|
}
|
|
if ( properties & NeedDestination ) {
|
|
_dest = PURL::Url(_args->url(argIndex));
|
|
argIndex++;
|
|
if ( !_force && _dest.exists() ) return errorExit(i18n("Destination file already exists."), FILE_ERROR);
|
|
}
|
|
if ( _device==0 && (properties & NeedDevice) ) return errorExit(i18n("Device not specified."), ARG_ERROR);
|
|
return OK;
|
|
}
|
|
|
|
CLI::ExitCode CLI::Main::executeCommand(const TQString &command)
|
|
{
|
|
if (_device) {
|
|
delete _memory;
|
|
_memory = _device->group().createMemory(*_device);
|
|
}
|
|
if ( command=="check" ) {
|
|
if ( _device==0 ) return okExit(i18n("Hex file is valid."));
|
|
TQStringList warnings;
|
|
Device::Memory::WarningTypes wtypes = _memory->fromHexBuffer(_source1, warnings);
|
|
if ( wtypes==Device::Memory::NoWarning ) return okExit(i18n("Hex file is compatible with device \"%1\".").arg(_device->name()));
|
|
return errorExit(warnings.join("\n"), EXEC_ERROR);
|
|
}
|
|
if ( command=="info" ) {
|
|
Log::KeyList keys;
|
|
keys.append(i18n("Format:"), HexBuffer::FORMATS[_format]);
|
|
uint nbWords = 0, start = 0, end = 0;
|
|
HexBuffer::const_iterator it;
|
|
for (it=_source1.begin(); it!=_source1.end(); ++it) {
|
|
if ( !it.data().isInitialized() ) continue;
|
|
nbWords++;
|
|
if ( nbWords==1 ) {
|
|
start = it.key();
|
|
end = it.key();
|
|
} else {
|
|
start = qMin(start, it.key());
|
|
end = qMax(end, it.key());
|
|
}
|
|
}
|
|
keys.append(i18n("No. of Words:"), toHexLabelAbs(nbWords));
|
|
if ( nbWords!=0 ) {
|
|
uint nbc = nbChars(NumberBase::Hex, end);
|
|
keys.append(i18n("Start:"), toHexLabel(start, nbc));
|
|
keys.append(i18n("End:"), toHexLabel(end, nbc));
|
|
}
|
|
keys.display(*_view);
|
|
return okExit(i18n("Hex file is valid."));
|
|
}
|
|
if ( command=="fix" ) {
|
|
for (uint i=0; i<uint(_errors.count()); i++)
|
|
if ( _errors[i].type==HexBuffer::UnrecognizedFormat ) return errorExit(i18n("Hex file cannot be fixed because the format was not recognized or is inconsistent."), EXEC_ERROR);
|
|
if ( _format==HexBuffer::Nb_Formats ) _format = HexBuffer::IHX32;
|
|
PURL::File dest(_dest, *_view);
|
|
if ( !dest.openForWrite() ) return FILE_ERROR;
|
|
_source1.savePartial(dest.stream(), _format);
|
|
_source1.saveEnd(dest.stream());
|
|
if ( !dest.close() ) return FILE_ERROR;
|
|
if ( _errors.isEmpty() ) return okExit(i18n("Hex file cleaned."));
|
|
return okExit(i18n("Hex file cleaned and fixed."));
|
|
}
|
|
if ( command=="compare" ) {
|
|
bool firstInSecond = true, secondInFirst = true;
|
|
HexBuffer::const_iterator it;
|
|
for (it=_source1.begin(); it!=_source1.end(); ++it) {
|
|
if ( it.data().maskWith(0xFFFF)==_source2[it.key()].maskWith(0xFFFF) ) continue;
|
|
firstInSecond = false;
|
|
}
|
|
for (it=_source2.begin(); it!=_source2.end(); ++it) {
|
|
if ( it.data().maskWith(0xFFFF)==_source1[it.key()].maskWith(0xFFFF) ) continue;
|
|
secondInFirst = false;
|
|
}
|
|
if ( firstInSecond && secondInFirst ) return okExit(i18n("The two hex files have the same content."));
|
|
if (firstInSecond) log(Log::LineType::Information, i18n("The first hex file is a subset of the second one."));
|
|
if (secondInFirst) log(Log::LineType::Information, i18n("The second hex file is a subset of the first one."));
|
|
return errorExit(i18n("The two hex files are different at address %1.").arg(toHexLabel(it.key(), 8)), EXEC_ERROR);
|
|
}
|
|
if ( command=="checksum" ) {
|
|
TQStringList warnings;
|
|
Device::Memory::WarningTypes wtypes = _memory->fromHexBuffer(_source1, warnings);
|
|
for (uint i=0; i<uint(warnings.count()); i++) log(Log::LineType::Warning, warnings[i]);
|
|
log(Log::LineType::Warning, i18n("Checksum computation is experimental and is not always correct!")); // #### REMOVE ME
|
|
BitValue cs = _memory->checksum();
|
|
log(Log::LineType::Normal, i18n("Checksum: %1").arg(toHexLabel(cs, 4)));
|
|
if ( _device->group().name()=="pic" ) {
|
|
BitValue ucs = static_cast<Pic::Memory *>(_memory)->unprotectedChecksum();
|
|
if ( ucs!=cs ) log(Log::LineType::Information, i18n("Unprotected checksum: %1").arg(toHexLabel(ucs, 4)));
|
|
}
|
|
return OK;
|
|
}
|
|
if ( command=="create" ) {
|
|
if ( _fill.isEmpty() || _fill=="blank" ) ; // default
|
|
else if ( _fill=="zero" ) _memory->fill(0x0);
|
|
else if ( _fill=="checksum_check" ) {
|
|
if ( _device->group().name()=="pic" ) static_cast<Pic::Memory *>(_memory)->checksumCheckFill();
|
|
} else {
|
|
bool ok;
|
|
uint value = fromAnyLabel(_fill, &ok);
|
|
Q_ASSERT(ok);
|
|
_memory->fill(value);
|
|
}
|
|
PURL::File dest(_dest, *_view);
|
|
if ( !dest.openForWrite() ) return FILE_ERROR;
|
|
_memory->save(dest.stream(), HexBuffer::IHX32);
|
|
if ( !dest.close() ) return FILE_ERROR;
|
|
return okExit(i18n("File created."));
|
|
}
|
|
Q_ASSERT(false);
|
|
return ARG_ERROR;
|
|
}
|
|
|
|
CLI::ExitCode CLI::Main::prepareRun(bool &interactive)
|
|
{
|
|
interactive = false;
|
|
return OK;
|
|
}
|
|
|
|
CLI::ExitCode CLI::Main::executeSetCommand(const TQString &property, const TQString &value)
|
|
{
|
|
if ( property=="device" || property=="processor" ) {
|
|
if ( value.isEmpty() ) {
|
|
_device = 0;
|
|
return OK;
|
|
}
|
|
TQString s = value.upper();
|
|
_device = Device::lister().data(s);
|
|
if ( _device==0 ) return errorExit(i18n("Unknown device \"%1\".").arg(s), ARG_ERROR);
|
|
return OK;
|
|
}
|
|
if ( property=="fill" ) {
|
|
_fill = value;
|
|
for (uint i=0; FILL_OPTIONS[i].name; i++)
|
|
if ( value==FILL_OPTIONS[i].name ) return OK;
|
|
bool ok;
|
|
(void)fromAnyLabel(value, &ok);
|
|
if ( !ok ) return errorExit(i18n("Number format not recognized."), ARG_ERROR);
|
|
return OK;
|
|
}
|
|
return errorExit(i18n("Unknown property \"%1\".").arg(property), ARG_ERROR);
|
|
}
|
|
|
|
TQString CLI::Main::executeGetCommand(const TQString &property)
|
|
{
|
|
if ( property=="device" || property=="processor" ) {
|
|
if ( _device==0 ) return i18n("<not set>");
|
|
return _device->name();
|
|
}
|
|
if ( property=="fill" ) {
|
|
if ( _fill.isEmpty() ) return i18n("<not set>");
|
|
return _fill;
|
|
}
|
|
log(Log::LineType::SoftError, i18n("Unknown property \"%1\".").arg(property));
|
|
return TQString();
|
|
}
|
|
|
|
CLI::ExitCode CLI::Main::list(const TQString &command)
|
|
{
|
|
if ( MainBase::list(command)==OK ) return OK;
|
|
if ( command=="device-list" ) return deviceList();
|
|
if ( command=="fill-list" ) return fillOptionList();
|
|
Q_ASSERT(false);
|
|
return OK;
|
|
}
|
|
|
|
CLI::ExitCode CLI::Main::deviceList()
|
|
{
|
|
TQValueVector<TQString> devices;
|
|
log(Log::LineType::Normal, i18n("Supported devices:"));
|
|
devices = Device::lister().supportedDevices();
|
|
qHeapSort(devices);
|
|
TQString s;
|
|
for (uint i=0; i<uint(devices.count()); i++) s += " " + devices[i];
|
|
log(Log::LineType::Normal, s + "\n");
|
|
return OK;
|
|
}
|
|
|
|
CLI::ExitCode CLI::Main::fillOptionList()
|
|
{
|
|
Log::KeyList keys(i18n("Fill options:"));
|
|
for (uint i=0; FILL_OPTIONS[i].name; i++)
|
|
keys.append(FILL_OPTIONS[i].name, i18n(FILL_OPTIONS[i].description));
|
|
keys.append(i18n("<value>"), i18n("Fill with the specified numeric value."));
|
|
keys.display(*_view);
|
|
return OK;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
int main(int argc, char **argv)
|
|
{
|
|
CLI::Main main;
|
|
Piklab::AboutData *about = new Piklab::AboutData("piklab-hex", I18N_NOOP("Piklab Hex Utility"), I18N_NOOP("Command-line utility to manipulate hex files."));
|
|
CLI::OptionList list = main.optionList(I18N_NOOP("Hex filename(s)."));
|
|
Piklab::init(about, argc, argv, false, list.ptr());
|
|
return main.doRun();
|
|
}
|