summaryrefslogtreecommitdiffstats
path: root/src/piklab-hex/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/piklab-hex/main.cpp')
-rw-r--r--src/piklab-hex/main.cpp291
1 files changed, 291 insertions, 0 deletions
diff --git a/src/piklab-hex/main.cpp b/src/piklab-hex/main.cpp
new file mode 100644
index 0000000..6323770
--- /dev/null
+++ b/src/piklab-hex/main.cpp
@@ -0,0 +1,291 @@
+/***************************************************************************
+ * 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 KCmdLineOptions OPTIONS[] = {
+ KCmdLineLastOption
+};
+
+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 KCmdLineOptions CLI::OPTIONS[] = {
+ KCmdLineLastOption
+};
+
+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 QString &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() ) {
+ QString s = (properties & NeedSource2 ? i18n("First hex file: ") : QString::null);
+ 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() ) {
+ QString s = (properties & NeedSource1 ? i18n("Second hex file: ") : QString::null);
+ 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 QString &command)
+{
+ if (_device) {
+ delete _memory;
+ _memory = _device->group().createMemory(*_device);
+ }
+ if ( command=="check" ) {
+ if ( _device==0 ) return okExit(i18n("Hex file is valid."));
+ QStringList 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" ) {
+ QStringList 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 QString &property, const QString &value)
+{
+ if ( property=="device" || property=="processor" ) {
+ if ( value.isEmpty() ) {
+ _device = 0;
+ return OK;
+ }
+ QString 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);
+}
+
+QString CLI::Main::executeGetCommand(const QString &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 QString::null;
+}
+
+CLI::ExitCode CLI::Main::list(const QString &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()
+{
+ QValueVector<QString> devices;
+ log(Log::LineType::Normal, i18n("Supported devices:"));
+ devices = Device::lister().supportedDevices();
+ qHeapSort(devices);
+ QString 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();
+}