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.
piklab/src/piklab-hex/main.cpp

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();
}