From 2ada69fe971ecc8881d5eb01dfbf337996472c40 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 1 Jul 2008 16:45:28 +0200 Subject: Initial import --- Makefile.in | 109 +++++ README | 48 +++ README.install | 25 ++ box.hpp | 50 +++ compareDlg.cpp | 297 ++++++++++++++ compareDlg.hpp | 56 +++ conversionDlg.cpp | 363 ++++++++++++++++ conversionDlg.hpp | 78 ++++ cursor.cpp | 158 +++++++ cursor.hpp | 71 ++++ debian/changelog | 5 + debian/compat | 1 + debian/control | 15 + debian/copyright | 33 ++ debian/docs | 1 + debian/lfhex.1 | 25 ++ debian/rules | 62 +++ debian/watch | 2 + delta.cpp | 126 ++++++ delta.hpp | 93 +++++ driver.cpp | 102 +++++ expr.h | 25 ++ expr.l | 154 +++++++ expr.y | 62 +++ gpl.txt | 108 +++++ grid.cpp | 42 ++ grid.hpp | 35 ++ hexEditor.cpp | 1112 ++++++++++++++++++++++++++++++++++++++++++++++++++ hexEditor.hpp | 242 +++++++++++ hexGui.cpp | 326 +++++++++++++++ hexGui.hpp | 97 +++++ images.qrc | 9 + img/exit.xbm | 6 + img/first.xbm | 6 + img/last.xbm | 6 + img/next.xbm | 6 + img/prev.xbm | 6 + lfhex.pro | 36 ++ local.h | 30 ++ mappings.h | 179 ++++++++ offsetConstraint.cpp | 98 +++++ offsetConstraint.hpp | 48 +++ reader.cpp | 363 ++++++++++++++++ reader.hpp | 95 +++++ save.cpp | 67 +++ save.hpp | 25 ++ translate.cpp | 189 +++++++++ translate.hpp | 47 +++ 48 files changed, 5139 insertions(+) create mode 100644 Makefile.in create mode 100644 README create mode 100644 README.install create mode 100644 box.hpp create mode 100644 compareDlg.cpp create mode 100644 compareDlg.hpp create mode 100644 conversionDlg.cpp create mode 100644 conversionDlg.hpp create mode 100755 cursor.cpp create mode 100755 cursor.hpp create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/docs create mode 100644 debian/lfhex.1 create mode 100755 debian/rules create mode 100644 debian/watch create mode 100755 delta.cpp create mode 100755 delta.hpp create mode 100644 driver.cpp create mode 100644 expr.h create mode 100644 expr.l create mode 100644 expr.y create mode 100644 gpl.txt create mode 100644 grid.cpp create mode 100644 grid.hpp create mode 100755 hexEditor.cpp create mode 100755 hexEditor.hpp create mode 100644 hexGui.cpp create mode 100644 hexGui.hpp create mode 100644 images.qrc create mode 100644 img/exit.xbm create mode 100644 img/first.xbm create mode 100644 img/last.xbm create mode 100644 img/next.xbm create mode 100644 img/prev.xbm create mode 100644 lfhex.pro create mode 100644 local.h create mode 100755 mappings.h create mode 100644 offsetConstraint.cpp create mode 100644 offsetConstraint.hpp create mode 100644 reader.cpp create mode 100644 reader.hpp create mode 100644 save.cpp create mode 100644 save.hpp create mode 100755 translate.cpp create mode 100755 translate.hpp diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..61e67cf --- /dev/null +++ b/Makefile.in @@ -0,0 +1,109 @@ +GREP = @GREP@ +MOC = @MOC@ +CXX = @CXX@ +FLEX = @FLEX@ +BISON = @BISON@ +INSTALL = @INSTALL@ + +TOPDIR = @top_srcdir@ +PROGRAM = @PROGRAM@ +VERSION = @VERSION@ +PROGRAM_PATH = $(TOPDIR)/bin/$(PROGRAM) +# +# allow user specified install path prefix +# +EXEC_PREFIX = @exec_prefix@ +ifeq ($(EXEC_PREFIX),) + EXEC_PREFIX=/usr/local/bin +endif + +CXXFLAGS = @CXXFLAGS@ +INCLUDE_DIRS = @QT_INC_DIR@ +IFLAGS = $(addprefix -I,$(INCLUDE_DIRS)) +LIBRARIES = QtCore Xext X11 m +LIB_DIRS = @QT_LIB_DIR@ /usr/X11R6/lib +LFLAGS = $(addprefix -L,$(LIB_DIRS)) $(addprefix -l,$(LIBRARIES)) + +CPP_SRC = $(wildcard *.cpp) expr.ll.cpp expr.yy.cpp +HPP_SRC = $(wildcard *.hpp) +H_SRC = $(wildcard *.h) $(HPP_SRC) +MOC_HPP = $(shell $(GREP) -l Q_OBJECT $(HPP_SRC)) + +MOC_SRC = $(addprefix moc_,$(MOC_HPP:.hpp=.cpp)) +CXX_SRC = $(CPP_SRC) $(MOC_SRC) + +OBJECTS = $(sort $(CXX_SRC:.cpp=.o)) + +BUILD_DATE = $(shell @DATE_PROG@) +BUILD_HOST = $(shell @HOSTNAME_PROG@) + +ifeq ($(VERBOSE),) + QUIET = @ +else + QUIET = +endif + +.PHONY : clean_moc clean_objects clean moc all build_info.h + +# +# rules +# +all : $(PROGRAM_PATH) + +$(PROGRAM_PATH) : $(OBJECTS) + @echo "Linking $(PROGRAM_PATH)" + $(QUIET) $(CXX) $(OBJECTS) $(LFLAGS) -o $@ + +app.pro : + echo "TEMPLATE = app" > $@ + echo "SOURCES = $(CPP_SRC)" >> $@ + echo "HEADERS = $(H_SRC)" >> $@ + echo "TARGET = $(PROGRAM)" >> $@ + echo "CONFIG = qt warn_on debug" >> $@ + +build_info.h : + echo #define BUILD_HOST "$(BUILD_HOST)" > $@ + echo #define BIULD_DATE "$(BUILD_DATE)" >> $@ + +moc : $(MOC_SRC) + +clean : clean_moc clean_objects + @echo "Removing objects and derived sources..." + $(QUIET) $(RM) core *~ .depend img/*~ + +clean_moc : + $(QUIET) $(RM) $(MOC_SRC) + +clean_objects : + $(QUIET) $(RM) $(OBJECTS) $(PROGRAM_PATH) + +depend : $(CXX_SRC) + @echo "Generating .depend file..." + $(QUIET) $(CXX) $(CXXFLAGS) $(IFLAGS) $^ -M -MG > .depend + +objects : $(OBJECTS) + +install : + @echo "Installing $(PROGRAM) in $(EXEC_PREFIX)" + $(QUIET) $(INSTALL) $(PROGRAM_PATH) $(EXEC_PREFIX) + +# pattern rules +moc_%.cpp : %.hpp + @echo "Generating $@..." + $(QUIET) $(MOC) $< -o $@ + +%.o : %.cpp + @echo "Compiling $<..." + $(QUIET) $(CXX) $(CXXFLAGS) $(IFLAGS) -c $< -o $@ + +# rules for flex/bison +expr.ll.cpp : expr.l expr.yy.hh + $(FLEX) -o$@ -Pexpr expr.l +expr.yy.cpp expr.yy.hh : expr.y + $(BISON) -d -b expr -p expr -o expr.yy.cpp $^ + +# +# Include make dependancies if they have been generated +# +-include .depend + diff --git a/README b/README new file mode 100644 index 0000000..40d2813 --- /dev/null +++ b/README @@ -0,0 +1,48 @@ +-------------------------- lfhex ------------------------------ +lfhex -> large file hex editor +Motivation: +Provide a fast/easy to use hex editor for viewing/modifying files which are +too large to hold in system memory. + +Highlights: +- supports large offsets (files > 2Gig) +- small memory footprint. Opening a 2gig file should only use an additional + 1.4megs. +- fast load times. +- fast save times. +- infinite undo/redo (as memory permits). +- conversion dialog (linked to selection). + o can byteswap data before conversion. +- search capability. +- scalable working area (resize is sane and can use as much screen as you give + it). +- runtime configurable editing mode (hex/octal/binary/ascii). +- runtime configurable bytes per column. +- binary comparison user interface (temporarily disabled in this version!): + o differences can be walked through byte by byte. + o offsets need not match between buffers. e.g: you can start the + comparison from one cursor position in the first file and use + a different cursor position in the second file. + o differences can be walked through block by block. + o blocks can be 1-16 bytes long and can be aligned with a + 0-15 byte offset. + +Limitations: +- no insertion/truncate. +- cannot search if file is modified. +- "Save As" is as slow as file copy. +- search times slow (on my Celeron 566 with 7.2k ide drive they take about + 1sec/Meg and is almost 3x slower if progressbar pops up). +- comparison mode is not allowed if either buffer has unsaved modifications. + +License: +This software is licensed under the GPL Version 2. + +Usage notes: +- to invoke comparison mode, put "-c" on the command line: +% lfhex -c file1 file2 +- you can enter multiple files on the command line. + +Please let me know if you find this software useful (or if it is crap :) +- Salem + diff --git a/README.install b/README.install new file mode 100644 index 0000000..d2f843e --- /dev/null +++ b/README.install @@ -0,0 +1,25 @@ +------------------ lfhex build/install instructions -------------------------- + +lfhex uses "qmake" for build management. + +Requirements: +- You must have Qt 4.x or latter to compile/link lfhex. +- You must have a compiler which is sane when using templates. +- You must have flex and bison + +Build Instructions: +% gunzip -c lfhex-x.x.tar.gz | tar xvf - +% cd lfhex-x.x +% qmake lfhex.pro +% make +% su +% make install + +If your platform does not support 64 bit file offsets then you will need to +comment out a line in lfhex.pro before building. + +If your platform is big-endian and you would like the offset labels to be in +big-endian you will have to uncomment a line in lfhex.pro before building. + +Please let me know if you have any difficulties building lfhex. +- Salem diff --git a/box.hpp b/box.hpp new file mode 100644 index 0000000..ae4973b --- /dev/null +++ b/box.hpp @@ -0,0 +1,50 @@ +#ifndef LFHEX_BOX_HPP +#define LFHEX_BOX_HPP +/* $Id: box.hpp,v 1.1 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +template< class _layout > +class box : public QWidget { +public: + box( QWidget* parent ) : QWidget(parent) { + QLayout* l = new _layout(this); + setLayout(l); + } + void addStretch( int s ) + { + dynamic_cast(layout())->addStretch(s); + } +protected: + void childEvent( QChildEvent *e ) { + if( e->added() && e->child()->isWidgetType() ) { + QWidget* w = dynamic_cast(e->child()); + layout()->addWidget(w); + } + } +}; + +typedef box hbox; +typedef box vbox; + +#endif + diff --git a/compareDlg.cpp b/compareDlg.cpp new file mode 100644 index 0000000..f8621f3 --- /dev/null +++ b/compareDlg.cpp @@ -0,0 +1,297 @@ +/* $Id: compareDlg.cpp,v 1.8 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compareDlg.hpp" +#include "offsetConstraint.hpp" +#include "hexGui.hpp" +#include "reader.hpp" +#include "box.hpp" + +// icon images: +#include "img/prev.xbm" +#include "img/next.xbm" +#include "img/last.xbm" +#include "img/first.xbm" +#include "img/exit.xbm" + +//extern QApplication * qApp; + +CompareDlg::CompareDlg(const char *file0, + const char *file1, + QWidget*parent) + : QMainWindow(parent) +{ + tabLayout = new QTabWidget(this); + setCentralWidget(tabLayout); + + QWidget *options = new QWidget(tabLayout); + QVBoxLayout *optionLayout = new QVBoxLayout(options); + + optionLayout->setMargin(3); + optionLayout->setSpacing(2); + + // setup alignment entries: + offsetConstraint = new OffsetConstraint(options); + offsetConstraint->setTitle( "Constrain offset to X*n+Y" ); + optionLayout->addWidget(offsetConstraint); + // setup cursor offsets + diffOffsets = new QCheckBox("Allow different file offsets",options); + optionLayout->addWidget(diffOffsets); + // add a dummy strechable widget for alignment. + optionLayout->addStretch(1); + connect(diffOffsets,SIGNAL(stateChanged(int)), + this,SLOT(setAllowDiffOffsets(int))); + diffOffsets->setChecked(false); + setAllowDiffOffsets(false); + + // setup toolbar + QToolBar *tools = new QToolBar(this); +/* FIXME new QToolButton(QBitmap::fromData(QSize(exit_width,exit_height),exit_bits), + "Exit","",qApp,SLOT(quit()),tools); + tools->addSeparator(); + new QToolButton(QBitmap(first_width,first_height,first_bits,true), + "First Difference","",this,SLOT(first()),tools); + new QToolButton(QBitmap(prev_width,prev_height,prev_bits,true), + "Previous Difference","?", + this,SLOT(prev()),tools); + new QToolButton(QBitmap(next_width,next_height,next_bits,true), + "Next Difference","foo", + this,SLOT(next()),tools); + new QToolButton(QBitmap(last_width,last_height,last_bits,true), + "Last Difference","",this,SLOT(last()),tools); +*/ + // setup compare section + vbox* view = new vbox(tabLayout); + + hexGui[0] = new HexGui(view); + hexGui[1] = new HexGui(view); + reader[0] = hexGui[0]->reader(); + reader[1] = hexGui[1]->reader(); + + if( file0 ) + hexGui[0]->open(file0); + if( file1 ) + hexGui[1]->open(file1); + + tabLayout->addTab(view,"&Compare"); + tabLayout->addTab(options,"&Options"); + addToolBar(tools); + // request a size large enough for both hexEditors + resize(500,500); + setWindowTitle("Compare dialog"); +} + +bool CompareDlg::enabled() +{ + if( reader[0]->is_open() && reader[1]->is_open() ) { + // check to see if modified!!! + if( hexGui[0]->isModified() || hexGui[1]->isModified() ) { + QMessageBox::critical(this,PROGRAM_STRING, + "Error, cannot use comparison" + " functions on modified buffers."); + return false; + } + return true; + } + + return false; +} +// public slots +void CompareDlg::setAllowDiffOffsets(int state) +{ + // diffOffsets->setChecked(state); +} + +bool CompareDlg::first() +{ + if( !enabled() ) + return false; + + // see Compare::last() for description of concept + off_t offset = offsetConstraint->next(0); + hexGui[0]->gotoOffset(offset); + hexGui[1]->gotoOffset(offset); + if( prev() ) + return true; + hexGui[0]->gotoOffset(0); + hexGui[1]->gotoOffset(0); + return next(); +} + +bool CompareDlg::last() +{ + if( !enabled() ) + return false; + + // look at lastidx 's previous's next range + // prev(x) is garranteed to not contain x, and next(prev(x)) will contain x + off_t min_size_decr = min( reader[0]->size()-1, + reader[1]->size()-1 ); + off_t offset = offsetConstraint->prev( min_size_decr ); + hexGui[0]->gotoOffset(offset); + hexGui[1]->gotoOffset(offset); + if( next() ) + return true; + hexGui[0]->gotoOffset( min_size_decr ); + hexGui[1]->gotoOffset( min_size_decr ); + return prev(); +} + +bool CompareDlg::prev() +{ + if( !enabled() ) + return false;; + // find previous diff + off_t offset[2]; + offset[0] = hexGui[0]->offset(); + offset[1] = (diffOffsets->isChecked()) ? hexGui[1]->offset() : offset[0]; + + if( offsetConstraint->getStride() > 1 ) { + off_t stop[2]; + off_t pos[2]; + for( pos[0] = offset[0] = offsetConstraint->prev(offset[0]), + pos[1] = offset[1] = offsetConstraint->prev(offset[1]); + offset[0] >= 0 && offset[1] >= 0; + pos[0] = offset[0] = offsetConstraint->prev(offset[0]), + pos[1] = offset[1] = offsetConstraint->prev(offset[1])) { + + // calculate stop + stop[0] = offset[0]+offsetConstraint->getStride(); + stop[1] = offset[1]+offsetConstraint->getStride(); + + // ignore lookups on less than a full stride length + // this would only happen if the file size is less than the stride + if( stop[0] > reader[0]->size() || stop[1] > reader[1]->size() ) + return false; + + while( pos[0] < stop[0] ) { + if( (*reader[0])[pos[0]++] != (*reader[1])[pos[1]++] ) { + // we have a difference + hexGui[0]->setSelection(offset[0],stop[0]); + hexGui[1]->setSelection(offset[1],stop[1]); + hexGui[0]->gotoOffset(offset[0]); + hexGui[1]->gotoOffset(offset[1]); + return true; + } + } + } + } else { + // just go one char at a time + + // make sure we are not past the eof of the second buffer + if( offset[1] >= reader[1]->size() ) + return false; + if( offset[0] <= 0 || offset[1] <= 0 ) + return false; + do { + offset[0]--; + offset[1]--; + + if( (*reader[0])[offset[0]] != (*reader[1])[offset[1]] ) { + hexGui[0]->gotoOffset(offset[0]); + hexGui[1]->gotoOffset(offset[1]); + hexGui[0]->setSelection(offset[0],offset[0]+1); + hexGui[1]->setSelection(offset[1],offset[1]+1); + return true; + } + } while( offset[0] > 0 && offset[1] > 0 ); + } + return false; +} + +bool CompareDlg::next() +{ + // find next diff + if( !enabled() ) + return false; + + off_t offset[2]; + offset[0] = hexGui[0]->offset(); + offset[1] = (diffOffsets->isChecked()) ? hexGui[1]->offset() : offset[0]; + + off_t size[2]; + size[0] = reader[0]->size(); + size[1] = reader[1]->size(); + + if( offsetConstraint->getStride() > 1 ) { + off_t stop[2]; + off_t pos[2]; + for( pos[0] = offset[0] = offsetConstraint->next(offset[0]), + pos[1] = offset[1] = offsetConstraint->next(offset[1]); + offset[0] < size[0] && offset[1] < size[1]; + pos[0] = offset[0] = offsetConstraint->next( offset[0] ), + pos[1] = offset[1] = offsetConstraint->next( offset[1] )) { + // compare from offset to offset+stride() + stop[0] = offset[0]+offsetConstraint->getStride(); + stop[1] = offset[1]+offsetConstraint->getStride(); + // if this will go past the bounds of the files, then the difference + // does not matter, ignore it. + if( stop[0] > size[0] || stop[1] > size[1] ) { + return false; + } + + while( pos[0] < stop[0] ) { + if( (*reader[0])[pos[0]++] != (*reader[1])[pos[1]++] ) { + // we have a difference + hexGui[0]->setSelection(offset[0],stop[0]); + hexGui[1]->setSelection(offset[1],stop[1]); + hexGui[0]->gotoOffset(offset[0]); + hexGui[1]->gotoOffset(offset[1]); + return true; // bail out of fn + } + } + } + } else { + // just go one at a time + if( offset[0] >= size[0] || offset[1] >= size[1] ) + return false; + if( offset[0] < 0 || offset[1] < 0 ) + return false; + offset[0]++; + offset[1]++; + while( offset[0] < size[0] && offset[1] < size[1] ) { + // don't compare the one we are on + if( (*reader[0])[offset[0]] != (*reader[1])[offset[1]] ) { + hexGui[0]->gotoOffset(offset[0]); + hexGui[1]->gotoOffset(offset[1]); + hexGui[0]->setSelection(offset[0],offset[0]+1); + hexGui[1]->setSelection(offset[1],offset[1]+1); + return true; + } + offset[0]++; + offset[1]++; + } + } + return false; +} diff --git a/compareDlg.hpp b/compareDlg.hpp new file mode 100644 index 0000000..ad1f6ab --- /dev/null +++ b/compareDlg.hpp @@ -0,0 +1,56 @@ +#ifndef COMPARE_DLG_HPP +#define COMPARE_DLG_HPP +/* $Id: compareDlg.hpp,v 1.5 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include "local.h" +// forward decl to speed compile times +class QCheckBox; +class QLineEdit; +class OffsetConstraint; +class HexGui; +class Reader; +class QTabWidget; + +class CompareDlg : public QMainWindow { + Q_OBJECT +public: + CompareDlg(const char * file0 = 0, + const char * file1 = 0, + QWidget * parent = 0); + + public slots: + void setAllowDiffOffsets(int state); + bool first(); + bool last(); + bool prev(); + bool next(); +protected: + bool enabled(); + +protected: + QTabWidget * tabLayout; + QCheckBox * diffOffsets; + HexGui * hexGui[2]; + Reader * reader[2]; + OffsetConstraint * offsetConstraint; +}; + +#endif diff --git a/conversionDlg.cpp b/conversionDlg.cpp new file mode 100644 index 0000000..29553f9 --- /dev/null +++ b/conversionDlg.cpp @@ -0,0 +1,363 @@ +/* $Id: conversionDlg.cpp,v 1.7 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "conversionDlg.hpp" +#include "local.h" +#include "grid.hpp" +#include "box.hpp" + +ConversionDlg::ConversionDlg ( QWidget*parent ) + : QWidget(parent) +{ + inValueChanged = false; + + + QVBoxLayout *vbox = new QVBoxLayout(this); + grid* g = new grid(3,this); + + g->setContentsMargins(3,3,3,3); + + vbox->addWidget(g); + + new QWidget(g); + new QLabel(" Hex:",g); + leditors[HexLE] = new QLineEdit(g); + + new QWidget(g); + new QLabel(" ASCII:",g); + leditors[AsciiLE] = new QLineEdit(g); + + byteSwapFlag[IntegerBS] = new QCheckBox("b/s",g); + new QLabel(" Int:",g); + leditors[IntegerLE] = new QLineEdit(g); + + byteSwapFlag[FloatBS] = new QCheckBox("b/s",g); + new QLabel(" Float:",g); + leditors[FloatLE] = new QLineEdit(g); + + byteSwapFlag[DoubleBS] = new QCheckBox("b/s",g); + new QLabel(" Double:",g); + leditors[DoubleLE] = new QLineEdit(g); + + // make sure to do an update if any of the byte swap flags toggle + for(int i = MinBS; i < MaxBS; ++i) { + connect(byteSwapFlag[i],SIGNAL(clicked()), + this,SLOT(valueChanged())); + // add tool tip + byteSwapFlag[i]->setToolTip("Byte Swap Data"); + } + + // setup validators + leditors[DoubleLE]->setValidator( new QDoubleValidator(this) ); + QDoubleValidator *vd = new QDoubleValidator(this); + vd->setRange( FLT_MAX, FLT_MAX ); + leditors[FloatLE]->setValidator( vd ); + leditors[IntegerLE]->setValidator( new QIntValidator(this) ); + leditors[HexLE]->setValidator( new HexValidator(this) ); + + // setup editor connections + connect(leditors[DoubleLE],SIGNAL(textChanged(const QString& )), + this,SLOT(doubleChanged(const QString&)) ); + connect(leditors[FloatLE],SIGNAL(textChanged(const QString& )), + this,SLOT(floatChanged(const QString&)) ); + connect(leditors[IntegerLE],SIGNAL(textChanged(const QString&)), + this,SLOT(integerChanged(const QString&)) ); + connect(leditors[HexLE],SIGNAL(textChanged(const QString&)), + this,SLOT(valueChanged(const QString&))); + connect(leditors[AsciiLE],SIGNAL(textChanged(const QString&)), + this,SLOT(asciiChanged(const QString&))); + + for( int i = MinLE; i < MaxLE; ++i ) { + connect(leditors[i],SIGNAL(returnPressed()), + this,SIGNAL(nextPressed())); + } + + hbox * h = new hbox(this); + vbox->addWidget(h); + vbox->addStretch(1); + QPushButton* prev = new QPushButton("<",h); + QPushButton* next = new QPushButton(">",h); + + connect(prev,SIGNAL(clicked()),this,SIGNAL(prevPressed())); + connect(next,SIGNAL(clicked()),this,SIGNAL(nextPressed())); + + setWindowTitle("Conversion Dialog"); +} + +ConversionDlg::~ConversionDlg() +{ + cout << "died" < bytes; + + if( byteSwapFlag[FloatBS]->isChecked() ) { + for( int i = sizeof(float)-1; i > -1; i--) + bytes.push_back(ptr[i]); + } else { + for( unsigned int i = 0; i < sizeof(float); i++ ) + bytes.push_back(ptr[i]); + } + + QString hex; + Translate::ByteToHex(hex,bytes); + + valueChanged( hex ); +} + +void ConversionDlg::doubleChanged(const QString& str) +{ + QChar lastCh = str[str.length()-1]; + if( lastCh == 'e' || lastCh == '-' || lastCh == '.' ) { + return; + } + + bool success; + double value = str.toDouble(&success); + + if( !success && !value ) { + return; + } + + // double to hex: + uchar * ptr = (uchar*)&value; + vector bytes; + + if(byteSwapFlag[DoubleBS]->isChecked() ) { + for(int i = sizeof(double)-1; i > -1; --i) + bytes.push_back(ptr[i]); + } else { + for(unsigned int i = 0; i < sizeof(double); i++ ) + bytes.push_back(ptr[i]); + } + QString hex; + Translate::ByteToHex(hex,bytes); + + valueChanged( hex ); +} + +void ConversionDlg::asciiChanged(const QString &str) +{ + vector bytes; + QString hex; + Translate::CharToByte(bytes,str); + Translate::ByteToHex(hex,bytes); + + valueChanged( hex ); +} + +void ConversionDlg::integerChanged( const QString & str ) +{ + bool success; + int value = str.toInt(&success); + + uchar * ptr = (uchar*) &value; + vector bytes; + + if( byteSwapFlag[IntegerBS]->isChecked() ) { + for( int i = sizeof(int)-1; i > -1 ;--i ) + bytes.push_back(ptr[i]); + } else { + for( unsigned int i = 0; i < sizeof(int)/sizeof(uchar); i++) + bytes.push_back(ptr[i]); + } + + QString hex; + Translate::ByteToHex(hex,bytes); + + valueChanged( hex ); +} + +// this slot just recalculates conversions +void ConversionDlg::valueChanged() +{ + // make sure the HexLE has focus, so valueChanged(str) updates all fields + leditors[HexLE]->setFocus(); + valueChanged( leditors[HexLE]->text() ); +} +// convert data to hex, then call updateData to set all the other data fields +void ConversionDlg::valueChanged( const QString& str ) +{ + if( str.size()%2 ) { + return; + } + // don't allow this call to nest + if(inValueChanged) + return; + + if( str.isEmpty() ) + return; + + inValueChanged = true; + // remmember cursor offset + int cursor_pos = leditors[HexLE]->cursorPosition(); + // conversion is safe because all chars are less than 'f' + leditors[HexLE]->setText( str ); + leditors[HexLE]->setCursorPosition( cursor_pos ); + + vector bytes; + Translate::HexToByte(bytes,str); + if( !leditors[AsciiLE]->hasFocus() ) { + + if(!bytes.size()) { + for(int i = MinLE; i < MaxLE; ++i) + leditors[i]->setText( "" ); + inValueChanged = 0; + return; + } + + // update the ascii entry: + QString ascii; + Translate::ByteToChar(ascii,bytes); + + leditors[AsciiLE]->setText( ascii ); + } + + // scratch conversion vars + QString strvalue; + uchar * ucharPtr; + + if( !leditors[IntegerLE]->hasFocus() ) { + // update the int entry: + // pad right with 0x00 + int intvalue = 0; + ucharPtr = (uchar*) &intvalue; + if( byteSwapFlag[IntegerBS]->isChecked() ) { + for(unsigned int i = 0; (i < sizeof(int)) && (i < bytes.size()); i++) + ucharPtr[sizeof(int)-1-i] = bytes[i]; + } else { + memcpy(&intvalue,&bytes.begin()[0], + min(sizeof(int),bytes.size())); + } + strvalue.setNum(intvalue); + leditors[IntegerLE]->setText( strvalue ); + } + + if( ! leditors[FloatLE]->hasFocus() ) { + // update the float entry: + float fvalue; + ucharPtr = (uchar*)(&fvalue); + if( byteSwapFlag[FloatBS]->isChecked() ) { + for(unsigned int i = 0; i < sizeof(float); ++i) { + if( i < bytes.size() ) { + ucharPtr[sizeof(float)-1-i] = bytes[i]; + } else { + ucharPtr[sizeof(float)-1-i] = 0x00; + } + } + } else { + if(bytes.size() < sizeof(float) ) { + for(unsigned int i= 0; i < sizeof(float); ++i) { + if( i < bytes.size() ) { + *ucharPtr++ = bytes[i]; + } else { + *ucharPtr++ = 0x00; + } + } + } else { + memcpy(&fvalue,&bytes.begin()[0],sizeof(float)); + } + } + strvalue.setNum( fvalue ); + leditors[FloatLE]->setText( strvalue ); + } + + if( ! leditors[DoubleLE]->hasFocus() ) { + // update the double entry: + double dvalue; + ucharPtr = (uchar*)&dvalue; + if( byteSwapFlag[DoubleBS]->isChecked() ) { + for(unsigned int i= 0; i < sizeof(double); ++i) { + if( i < bytes.size() ) { + ucharPtr[sizeof(double)-1-i] = bytes[i]; + } else { + ucharPtr[sizeof(double)-1-i] = 0x00; + } + } + } else { + if(bytes.size() < sizeof(double) ) { + for(unsigned int i= 0; i < sizeof(double); ++i) { + if( i < bytes.size() ) { + *ucharPtr++ = bytes[i]; + } else { + *ucharPtr++ = 0x00; + } + } + } else { + memcpy(&dvalue,&bytes.begin()[0],sizeof(double)); + } + } + strvalue.setNum( dvalue ); + leditors[DoubleLE]->setText( strvalue ); + } + + inValueChanged = false; +} + +QString ConversionDlg::hexValue() const +{ + return leditors[HexLE]->text(); +} + +HexValidator::HexValidator( QWidget * parent ) + : QValidator(parent) +{ +} + +// allow 0xfff syntax +QValidator::State HexValidator::validate ( QString &str, int &pos ) const +{ + str = str.toLower(); + for(int i = 0; i < pos; i++) { + if( ! ((str[i] >= 'a' && str[i] <= 'f') || + (str[i] >= '0' && str[i] <= '9') ) && + ! (i == 1 && str[i] == 'x' ) + ) { + return Invalid; + } + } + return Acceptable; +} + diff --git a/conversionDlg.hpp b/conversionDlg.hpp new file mode 100644 index 0000000..734a59b --- /dev/null +++ b/conversionDlg.hpp @@ -0,0 +1,78 @@ +/* $Id: conversionDlg.hpp,v 1.4 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _CONVERSION_DLG_HPP_ +#define _CONVERSION_DLG_HPP_ +#include +#include +#include + +#include "translate.hpp" + +class QLineEdit; +class QCheckBox; + +class ConversionDlg : public QWidget { + Q_OBJECT +public: + ConversionDlg(QWidget* parent=0); + ~ConversionDlg(); + + QString hexValue() const; + +signals: + void nextPressed(); + void prevPressed(); + +public slots: + void doubleChanged( const QString& str ); + void floatChanged( const QString& str ); + void integerChanged( const QString& str ); + void valueChanged( const QString& str ); + void valueChanged(); + void asciiChanged( const QString& str ); + +protected: + enum { + MinLE = 0, + HexLE = 0 , + AsciiLE, + DoubleLE, + FloatLE, + IntegerLE, + MaxLE + }; + enum { + MinBS =0, + DoubleBS=0, + FloatBS, + IntegerBS, + MaxBS + }; + QLineEdit * leditors[MaxLE]; + QCheckBox * byteSwapFlag[MaxBS]; + bool inValueChanged; +}; + +class HexValidator : public QValidator { +public: + HexValidator( QWidget* parent ); + State validate( QString & str, int & pos ) const; +}; + +#endif diff --git a/cursor.cpp b/cursor.cpp new file mode 100755 index 0000000..cef8c7a --- /dev/null +++ b/cursor.cpp @@ -0,0 +1,158 @@ +/* $Id: cursor.cpp,v 1.3 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "local.h" +#include "cursor.hpp" + +// +// Cursor implimentation +// + +Cursor::Cursor(off_t byteOffset, + off_t charOffset, + off_t low, + off_t high, + off_t charsPerByte) +: _byteOffset(byteOffset), + _charOffset(charOffset), + _low(low), + _high(high), + _charsPerByte(charsPerByte) +{} +// note: range includes low but excludes high: [low,high) (like stl) +// note: [foo,foo) is considered the empty set +bool Cursor::setRange( off_t low, off_t high ) +{ + if ( low > high ) + return false; + _low = low; + _high = high; + return true; +} + +off_t Cursor::decrByByte( off_t n ) +{ + // check for underflow + if (n > _byteOffset) + return OutOfRange; + else + _byteOffset -= n; + return _byteOffset; +} +off_t Cursor::decrByChar( off_t n ) +{ + off_t byteDelta = n/_charsPerByte; + off_t charDelta = n%_charsPerByte; + + if ( charDelta > _charOffset ) { + if( byteDelta == MAX_OFFSET ) { + throw out_of_range("Cursor decriment underflowed.\n"); + } + byteDelta++; + } + // if underflow.. then just return + if( byteDelta && OutOfRange == decrByByte( byteDelta ) ) { + return _charOffset; + } + if ( charDelta > _charOffset ) { + // offset == maxidx - abs(_charOffset - charDelta) + _charOffset = (_charsPerByte) - (charDelta - _charOffset); + } else { + _charOffset -= charDelta; + } + return _charOffset; +} +// note: off_t is always positive +off_t Cursor::incrByChar( off_t n ) +{ + off_t byteDelta = n/_charsPerByte; + off_t charDelta = n%_charsPerByte; + + // carry + if ( charDelta + _charOffset >= _charsPerByte ) { + // Note: this cannot overflow unless chars perbyte == 1 + if ( byteDelta == MAX_OFFSET ) { + throw out_of_range("Cursor increment exceeded maximum offset.\n"); + } + byteDelta++; + } + // if out of range, just leave the char offset where it is + if( byteDelta && OutOfRange == incrByByte( byteDelta ) ) { + return _charOffset; + } + // limit _charOffset to _charsPerByte - 1 + _charOffset = (charDelta + _charOffset)%_charsPerByte; + return _charOffset; +} + +off_t Cursor::incrByByte( off_t byteDelta ) +{ + if ( byteDelta > MAX_OFFSET - _byteOffset ) { + throw out_of_range("Cursor increment exceeded maximum offset.\n"); + } + if ( _byteOffset + byteDelta >= _high ) + return OutOfRange; + return (_byteOffset += byteDelta); +} + +// +// accessor implimentations +// +off_t Cursor::byteOffset() const +{ + return _byteOffset; +} +off_t Cursor::charOffset() const +{ + return _charOffset; +} +#define MIN(a,b) (a < b)?a:b +off_t Cursor::setCharsPerByte( off_t charsPerByte ) +{ + _charsPerByte = charsPerByte; + _charOffset = MIN(_charOffset,charsPerByte-1); + return _charsPerByte; +} +// _byteOffset values are clamped to: +// [_low,_high) +// _charOffset values are clamped to: +// [0,_charsPerByte-1] +void Cursor::setOffset( off_t byteOffset, off_t charOffset ) +{ + assignClamped(_byteOffset,byteOffset,0,_high); + assignClamped(_charOffset,charOffset,0,_charsPerByte); +} +off_t Cursor::charOffsetAbsolute() const +{ + return _byteOffset*_charsPerByte + _charOffset; +} +// +// void Cursor::assignClamped(dst,src, low,high); +// convienience function for assigning dst = src +// clamped to [low,high) +// +void Cursor::assignClamped(off_t& dst, off_t src, off_t low, off_t high) +{ + if( src < low ) { + dst = low; + } else if( src >= high ) { + dst = high-1; + } else { + dst = src; + } +} diff --git a/cursor.hpp b/cursor.hpp new file mode 100755 index 0000000..eca4ac0 --- /dev/null +++ b/cursor.hpp @@ -0,0 +1,71 @@ +/* $Id: cursor.hpp,v 1.4 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _CURSOR_HPP_ +#define _CURSOR_HPP_ + +#include +#include +#include + +#ifdef _LARGEFILE_SOURCE +#define MAX_OFFSET LONG_LONG_MAX +#else +#define MAX_OFFSET LONG_MAX +#endif + +class Cursor { +public: + Cursor( off_t byteOffset = 0, + off_t charOffset = 0, + off_t low = 0, + off_t high = 1, + off_t charsPerByte = 2 ); // defaults to hex + Cursor( const Cursor& c); + + off_t incrByChar( off_t n ); + off_t incrByByte( off_t n ); + off_t decrByChar( off_t n ); + off_t decrByByte( off_t n ); + + off_t byteOffset() const; //returns the current byte offset + off_t charOffset() const; //returns the current char offset. (relative) + off_t charOffsetAbsolute() const; //returns the current absolute offset. + + bool setRange( off_t low, off_t high ); + // setCharsPerByte: + // sideEffect: sets charOffset to min(_charsPerByte-1,_charOffset) + off_t setCharsPerByte( off_t charsPerByte ); + void setOffset( off_t byteOffset, off_t charOffset ); + +protected: + // performs: dst = src; and clamps the value to [low,high) + void assignClamped(off_t& dst,off_t src,off_t low,off_t high); + + enum { + OutOfRange = -1, + }; +protected: + off_t _byteOffset; + off_t _charOffset; + off_t _low; + off_t _high; + off_t _charsPerByte; +}; + +#endif diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..3ae4baf --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +lfhex (0.4-1) unstable; urgency=low + + * Initial release (Closes: #483159, #483165) + + -- Tobias Klauser Tue, 27 May 2008 19:49:49 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..1e8b314 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +6 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..2b11c65 --- /dev/null +++ b/debian/control @@ -0,0 +1,15 @@ +Source: lfhex +Section: editors +Priority: optional +Maintainer: Tobias Klauser +Build-Depends: debhelper (>= 6), libqt4-dev, flex, bison +Standards-Version: 3.8.0 +Homepage: http://stoopidsimple.com/lfhex + +Package: lfhex +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: large file hex editor + lfhex is an application for viewing and editing files in hex, octal, + binary, or ascii text. The main strength of lfhex is its ability to + work with files much larger than system memory or address space. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..688ba79 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,33 @@ +This package was debianized by Tobias Klauser on +Tue, 27 May 2008 18:56:54 +0200. + +It was downloaded from . + +Upstream Author: + + Salem Ganzhorn + +Copyright: + + Copyright (C) 2006 Salem Ganzhorn + +License: + + 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 version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +On Debian systems, the complete text of the GNU General Public License +can be found in /usr/share/common-licenses/GPL file. + +The Debian packaging is (C) 2008, Tobias Klauser and +is licensed under the GPL, see `/usr/share/common-licenses/GPL'. diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..e845566 --- /dev/null +++ b/debian/docs @@ -0,0 +1 @@ +README diff --git a/debian/lfhex.1 b/debian/lfhex.1 new file mode 100644 index 0000000..199b1d2 --- /dev/null +++ b/debian/lfhex.1 @@ -0,0 +1,25 @@ +.TH LFHEX 1 "May 27, 2008" +.SH NAME +lfhex \- Large File Hex Editor +.SH SYNOPSIS +.B lfhex +.RI [ options ] " files" ... +.SH DESCRIPTION +\fBlfhex\fP is an application for viewing and editing files in hex, octal, +binary, or ascii text. The main strength of lfhex is its ability to work with +files much larger than system memory or address space. +.SH OPTIONS +.B \-c \fIfile1\fR \fIfile2\fR +Start lfhex in compare mode +.PP +lfhex supports some traditional X11 command line options supported through Qt's +QApplication class. See +\fBhttp://doc.trolltech.com/4.1/qapplication.html#QApplication for details.\fP +.SH SEE ALSO +.PP +The lfhex manual at: \fBhttp://stoopidsimple.com/lfhex/manual\fP +.SH AUTHOR +lfhex was written by Salem Ganzhorn . +.PP +This manual page was written by Tobias Klauser , +for the Debian project (but may be used by others). diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..9111682 --- /dev/null +++ b/debian/rules @@ -0,0 +1,62 @@ +#!/usr/bin/make -f + +configure: configure-stamp +configure-stamp: + dh_testdir + + qmake lfhex.pro + + touch configure-stamp + +build: build-stamp + +build-stamp: configure-stamp + dh_testdir + + $(MAKE) + + touch $@ + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + [ ! -f Makefile ] || $(MAKE) clean + rm -f Makefile lfhex + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Do a manual install, qmake does not correctly generate an install + # target + install -D -m755 -p lfhex $(CURDIR)/debian/lfhex/usr/bin/lfhex + +# Build architecture-independent files here. +binary-indep: +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installdocs + dh_installchangelogs + dh_installman debian/lfhex.1 + dh_link + dh_strip + dh_compress + dh_fixperms + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..a0bc5e3 --- /dev/null +++ b/debian/watch @@ -0,0 +1,2 @@ +version=3 +http://stoopidsimple.com/files/lfhex-(.*)\.tar\.gz diff --git a/delta.cpp b/delta.cpp new file mode 100755 index 0000000..eb7e649 --- /dev/null +++ b/delta.cpp @@ -0,0 +1,126 @@ +/* $Id: delta.cpp,v 1.4 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "delta.hpp" + +// +// DeltaMap class implimentation: +// + +DeltaMap::DeltaMap() {} // noop +DeltaMap::~DeltaMap() +{ + // just free all of the Delta's + clear(); +} + +// Free all the Delta's +// Note: this invalidates all pointers to Delta's returned through search() +// do NOT keep returned pointers arround for long. +// +void DeltaMap::clear() +{ + _map.erase(_map.begin(),_map.end()); + + // note: the union of _undo and _redo should contain ALL of the Delta's + // in the map. The intersection of _undo and _redo should be the empty set. + while ( !_undo.empty() ) { + delete _undo.top(); + _undo.pop(); + } + while ( !_redo.empty() ) { + delete _redo.top(); + _redo.pop(); + } +} + +// returns const ptr to the most recent delta on offset off_t +const Delta * DeltaMap::search( off_t offset ) const +{ + delta_map_t::const_iterator itr = _map.find(offset); + if ( itr != _map.end() && (itr->second).size() ) + return itr->second.back(); + else + return 0; +} + +size_t DeltaMap::insert( off_t offset, + const vector& oldData, + const vector& newData ) +{ + // + // only modify the newData of the top undo iff the offsets match + // and the redo stack is empty. + // + if( _undo.size() && _undo.top()->offset() == offset && _redo.empty() ) { + //just modify the newData of the top delta + _undo.top()->setNewData( newData ); + + // _map[offset] _should_ exist + return _map[offset].size(); + } else { + // create a new delta + Delta * tmp = new Delta(offset, oldData, newData); + _map[offset].push_back( tmp ); + _undo.push( tmp ); + return _map[offset].size(); + } +} + +bool DeltaMap::undo() +{ + if( _undo.empty() ) + return false; + + Delta* d = _undo.top(); + _undo.pop(); + // find the delta in the delta list indexed by d->offset and remove it. + map >::iterator itr = _map.find(d->offset()); + itr->second.remove(d); + // remove the entry iff the list is empty + if( itr->second.empty() ) { + _map.erase(itr); + } + // push the delta back onto the redo stack. + _redo.push( d ); + return true; +} + +bool DeltaMap::redo() +{ + if( _redo.empty() ) + return false; + + // insert the delta back into the modification list + Delta *d = _redo.top(); + _redo.pop(); + _map[d->offset()].push_back( d ); + _undo.push( d ); + return true; +} + +// methods for iterating over all the modifications in the tree +off_t DeltaMap::lower_bound(off_t lb) const +{ + delta_map_t::const_iterator itr = _map.lower_bound(lb); + if( itr == _map.end() ) { + return -1; + } else { + return itr->first; + } +} diff --git a/delta.hpp b/delta.hpp new file mode 100755 index 0000000..502419f --- /dev/null +++ b/delta.hpp @@ -0,0 +1,93 @@ +/* $Id: delta.hpp,v 1.5 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _DELTA_MAP_ +#define _DELTA_MAP_ + +#include +#include +#include +#include +#include + +#include + +#include "local.h" + +class Delta { +public: + Delta( off_t i, const vector& oldData, const vector& newData) : + _offset(i), _old(oldData), _new(newData) {} + + void setNewData( const vector& ndata ) { _new = ndata; } + + off_t offset() const { return _offset; } + const vector& newData() const { return _new;} + const vector& oldData() const { return _old;} +protected: + off_t _offset; + vector _old; + vector _new; +}; + + +// allocation for all delta's is dynamic, but it is all handled +// within the class. +// the user of the class does not need to worry about memory +// allocation/deallocation. +class DeltaMap { +public: + DeltaMap(); + ~DeltaMap(); + +public: + // insert a new delta + size_t insert(off_t offset, + const vector& oldData, + const vector& newData); + // return the offset of the last inserted Delta. + off_t lastOffset() const; + // set the last delta's newData to data + void setLastData( const vector& data); + int numEdits() const { return _undo.size(); } + // find the most recent delta + const Delta* search(off_t offset) const; + + // cleans undo/redo stacks and frees memory for all deltas. + // Makes references to delta's invalid. + void clear(); + + bool undo(); //undo last insert + bool redo(); //redo last insert + + // method for iterating over all of the modifications + // finds the first offset that is is not less than lb + // so lower_bound(0) returns the offset of the first delta + // (off_t) -1 is returned on failure + off_t lower_bound(off_t lb) const; + +protected: + typedef map > delta_map_t; + // notes: + // the back of the delta list is always the most recent/non undone delta. + map > _map; + stack _undo; + stack _redo; +}; + +#endif diff --git a/driver.cpp b/driver.cpp new file mode 100644 index 0000000..a52ac0a --- /dev/null +++ b/driver.cpp @@ -0,0 +1,102 @@ +/* $Id: driver.cpp,v 1.8 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +extern "C" { + int optind; +}; +#include "hexGui.hpp" +#include "local.h" +#include "compareDlg.hpp" + +#define STUB(x) extern "C" { void x() {}} +STUB(glXCreateGLXPixmapMESA); +STUB(glXReleaseBuffersMESA); + +// this is used for debugging because I cannot seem to figure out how to see +// the asci representation of a QString while in the debugger. +#include +void coutAsciText( const QString& str ) +{ + cout << &str << " -> \"" << C_STR(str) << "\"" << endl; +} + +int main (int argc, char ** argv) +{ + QApplication a(argc,argv); + a.setStyle( "plastique" ); + // parse the command line and see what major mode we should be in + int compare = false; + int c; + // skip argv[0] + optind = 1; + while( EOF != (c = getopt(argc,argv,"c")) ) { + switch (c) { + case 'c': + compare = true; + break; + default: + cerr << "usage: " PROGRAM " [-c [file1 file2]] files..." <= 2 ) { + (new CompareDlg(argv[optind],argv[optind+1]))->show(); + optind+=2; + } else { + (new CompareDlg())->show(); + } + // treat remaining cmdline options as files to be opened. + while( optind < argc ) { + HexGui *hg = new HexGui(); + hg->open(argv[optind++]); + hg->show(); + } + } else { + // each remaining argument is assumed to be a file + if( optind >= argc ) { + // no files, just open a hexGui + (new HexGui())->show(); + } else { + while( optind < argc ) { + HexGui *hg = new HexGui(); + hg->open(argv[optind++]); + hg->show(); + } + } + } + + a.connect( &a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()) ); + int ret=0; + try { + ret = a.exec(); + } catch (const exception& e) { + ret = 1; + cerr << "[error] - unhandled exeption in main:\"" << e.what() << "\"" < + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +bool expr_eval( const std::string &str, off_t &value ); + +#endif diff --git a/expr.l b/expr.l new file mode 100644 index 0000000..a94cfe3 --- /dev/null +++ b/expr.l @@ -0,0 +1,154 @@ +%{ +/* $Id: expr.l,v 1.4 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include + +#include +#include +#include + +using namespace std; + +#include "expr_yacc.h" +#include "expr.h" + +int exprparse(); + +extern off_t expr_value; + +static off_t hex2l( const string &str ); + +#define yylval exprlval + +%} + +%option noyywrap + +hexnum [0-9a-fA-F]+ + +%% + +[1-9][0-9]* { + yylval = atol(yytext); + return NUMBER; + } +{hexnum} | +0x{hexnum} { + string hexnum = yytext; + if( hexnum.substr(0,2) == "0x" ) { + hexnum = hexnum.substr(2); + } + yylval = hex2l(hexnum); + return NUMBER; + } +[-+*()] { return yytext[0]; } +[ \r\n\t] {/*nop*/} +. { return yytext[0]; } + +%% + +static off_t +hex2l( + const string &str + ) +{ + // + off_t ret = 0; + size_t scale = 1; + int i = str.size()-1; + while( i > -1 ) { + switch( str[i] ) { + case '0': + break; + case '1': + ret += 1*scale; + break; + case '2': + ret += 2*scale; + break; + case '3': + ret += 3*scale; + break; + case '4': + ret += 4*scale; + break; + case '5': + ret += 5*scale; + break; + case '6': + ret += 6*scale; + break; + case '7': + ret += 7*scale; + break; + case '8': + ret += 8*scale; + break; + case '9': + ret += 9*scale; + break; + case 'a': + case 'A': + ret += 10*scale; + break; + case 'b': + case 'B': + ret += 11*scale; + break; + case 'c': + case 'C': + ret += 12*scale; + break; + case 'd': + case 'D': + ret += 13*scale; + break; + case 'e': + case 'E': + ret += 14*scale; + break; + case 'f': + case 'F': + ret += 15*scale; + break; + default: + char buf[1024]; + sprintf(buf,"Unknown hex char: \"%s\"[%d]",str.c_str(),i); + throw runtime_error(buf); + } + --i; + scale *= 16; + } + return ret; +} + +bool +expr_eval( + const string &str, + off_t &retval + ) +{ +// exprrestart(NULL); + expr_scan_string(str.c_str()); + expr_value = 0; + bool status = !exprparse(); + retval = expr_value; + return status; +} + + diff --git a/expr.y b/expr.y new file mode 100644 index 0000000..84fe0dc --- /dev/null +++ b/expr.y @@ -0,0 +1,62 @@ +%{ +/* $Id: expr.y,v 1.2 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#define YYSTYPE off_t +#include + +#include + +#include "expr.h" + +using namespace std; + +off_t expr_value; + +int exprlex(); + +static int exprerror( const char * ); + +%} + +%token NUMBER +%left '-' '+' '*' '/' + +%% +target : expr { + expr_value = $1; +} +; + +expr: /* nothing */ +| NUMBER { $$ = $1; } +| expr '+' expr { $$ = $1 + $3; } +| expr '-' expr { $$ = $1 - $3; } +| expr '*' expr { $$ = $1 * $3; } +| '(' expr ')' { $$ = $2; } +; + + +%% + +static int +exprerror( + const char * str + ) +{ + return 0; +} diff --git a/gpl.txt b/gpl.txt new file mode 100644 index 0000000..b5ab307 --- /dev/null +++ b/gpl.txt @@ -0,0 +1,108 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + + + Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + + + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + +2a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + +2b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + +2c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + +3a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + +3b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + +3c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + + +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + + +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + + +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + + +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + + + NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + + END OF TERMS AND CONDITIONS diff --git a/grid.cpp b/grid.cpp new file mode 100644 index 0000000..643b7d8 --- /dev/null +++ b/grid.cpp @@ -0,0 +1,42 @@ +/* $Id: grid.cpp,v 1.1 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "grid.hpp" +#include +#include + +grid::grid( int cols, QWidget* parent ) + : QWidget(parent), m_cols(cols), m_row(0), m_col(0) +{ + + setLayout( new QGridLayout(this) ); +} + +void grid::childEvent( QChildEvent* e ) +{ + if( e->added() && e->child()->isWidgetType() ) { + QWidget* w = dynamic_cast(e->child()); + QGridLayout* l = dynamic_cast(layout()); + l->addWidget(w,m_row,m_col); + ++m_col; + if( m_col >= m_cols ) { + m_col = 0; + ++m_row; + } + } +} diff --git a/grid.hpp b/grid.hpp new file mode 100644 index 0000000..c07c340 --- /dev/null +++ b/grid.hpp @@ -0,0 +1,35 @@ +#ifndef LFHEX_GRID_HPP +#define LFHEX_GRID_HPP +/* $Id: grid.hpp,v 1.1 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include + +class grid : public QWidget { +public: + grid( int cols, QWidget* parent ); + +protected: + void childEvent( QChildEvent *e ); + +protected: + int m_cols; + int m_row; + int m_col; +}; + +#endif diff --git a/hexEditor.cpp b/hexEditor.cpp new file mode 100755 index 0000000..0cae378 --- /dev/null +++ b/hexEditor.cpp @@ -0,0 +1,1112 @@ +/* $Id: hexEditor.cpp,v 1.14 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hexEditor.hpp" +#include "translate.hpp" +#include "local.h" + +extern int errno; + +HexEditor::HexEditor( QWidget * parent ) + : QWidget(parent) +{ + _cols = 5; + _rows = 10; + _charsPerByte = 2; + _base = 16; + _topLeft = 0; + _topMargin = _wordSpacing = 6; + _bytesPerWord = 1; + _lastValidWord = -1; + _selection[SelectionStart] = _selection[SelectionEnd] = -1; + + setFocusPolicy(Qt::StrongFocus); + // the first setBytesPerWord should not come before the first setFont() + QFont font("fixed"); + font.setFixedPitch(1); + setFont( font ); + setBackgroundRole( QPalette::Base ); +} + +HexEditor::~HexEditor() +{ + _reader.close(); +} + +bool HexEditor::isModified() const +{ + return ( _reader.is_open() && _delta.numEdits() ); +} + +bool HexEditor::save( QString filename ) +{ + if( !isModified() ) { + QString errMsg = "Error, file is not open or has not been modified. " + "Aborting save."; + QMessageBox::warning(this,PROGRAM_STRING,errMsg); + return false; + } + + if( filename != "" ) { + // copy open reader file to filename + QString oldFilename = _reader.filename(); + + if( oldFilename == "" ) + return false; + + _reader.close(); + FILE* fin = fopen(C_STR(oldFilename),"r"); + if( !fin ) { + QString errMsg = "Error opening \"" + oldFilename + "\" for reading: " + + strerror(errno); + QMessageBox::warning(this,PROGRAM_STRING,errMsg); + return false; + } + FILE* fout = fopen(C_STR(filename),"w"); + if( !fout ) { + QString errMsg = "Error opening \"" + filename+ "\" for writing: " + + strerror(errno); + QMessageBox::warning(this,PROGRAM_STRING,errMsg); + fclose(fin); + return false; + } + int length; + uchar bytes[4096]; + while( (length = fread(bytes,1,4096,fin)) ) { + fwrite( bytes, 1, length, fout ); + } + fclose(fin); + fclose(fout); + } else { + filename = _reader.filename(); + _reader.close(); + } + + if( filename == "" ) + return false; + + if( !_delta.numEdits() ) + return false; + + if( !writeDeltas(C_STR(filename),_delta) ) { + _reader.open(C_STR(filename)); + return false; + } + _delta.clear(); + _reader.open(C_STR(filename)); + setTopLeft(_topLeft); + return true; +} + +QString HexEditor::filename() const +{ + return _reader.filename(); +} + +bool HexEditor::open( const QString & filename ) +{ + if( closeFile() == QMessageBox::Cancel ) + return false; + + if(!_reader.open(C_STR(filename))) { + QMessageBox::critical( this, "HexEdit", + "Error loading \"" + filename + "\"\n" + + _reader.lastError(), + QMessageBox::Ok,0); + return false; + } + _delta.clear(); // reset modification tree + + // set the new range for the scrollbar + _cursor.setRange(0,_reader.size()); + _cursor.setCharsPerByte(_charsPerByte); + setSelection(SelectionStart,-1); + setSelection(SelectionEnd,-1); + emit rangeChanged(0,_reader.size()/bytesPerLine()); + calculateFontMetrics(); // update labels + setTopLeft(0); // reset the GUI + return true; +} + +int HexEditor::closeFile(bool force) +{ + int retval = QMessageBox::No; + if( _delta.numEdits() ) { + QString message = + "Save changes to \"" + QString(_reader.filename()) + "\"?"; + if( force ) { + retval = QMessageBox::warning( this, + PROGRAM_STRING, + message, + QMessageBox::Yes, + QMessageBox::No ); + } else { + retval = QMessageBox::warning( this, + PROGRAM_STRING, + message, + QMessageBox::Yes, + QMessageBox::No, + QMessageBox::Cancel); + } + } + if( retval == QMessageBox::Yes ) + save(); + return retval; +} + +void HexEditor::setBytesPerWord( int nbytes ) +{ + _bytesPerWord = nbytes; + calculateFontMetrics(); + QResizeEvent *re = new QResizeEvent(QSize(size()),QSize(size())); + resizeEvent(re); + delete re; +} +int HexEditor::fontHeight() const +{ + return _fontHeight; +} +int HexEditor::lineSpacing() const +{ + return _lineSpacing; +} +int HexEditor::fontMaxWidth() const +{ + return _fontMaxWidth; +} +void HexEditor::calculateFontMetrics() +{ + _lineSpacing = fontMetrics().lineSpacing(); + _fontMaxWidth = fontMetrics().maxWidth(); + _wordWidth = _fontMaxWidth*charsPerWord(); + _fontHeight = fontMetrics().height(); + // see how many bytes are needed to show the size of this file + // log base 16 -> log16(x) = log(x)/log(16) + if( _reader.size() ) { + double width = log(static_cast(_reader.size()))/log(16.0)+2; + _offsetLabelBytes = static_cast( width ); + } else { + _offsetLabelBytes = 0; + } + _leftMargin = _topMargin + _fontMaxWidth*(_offsetLabelBytes + 2); + // make sure bboxes are updated with new offset subdivision + QResizeEvent *re = new QResizeEvent(QSize(size()),QSize(size())); + resizeEvent(re); + delete re; +} +void HexEditor::setFont(const QFont& font ) +{ + if( !font.fixedPitch() ) { + cerr << "setFont() failed, font was not fixedPitch()." << endl; + return; + } + QWidget::setFont(font); + calculateFontMetrics(); +} +// set the top left editor to offset in reader +void HexEditor::setTopLeft( off_t offset ) +{ + static bool inTopLeft; + if( inTopLeft ) { + // don't nest + return; + } + inTopLeft = true; + try { + if( offset < 0 ) { + _topLeft = 0; + } else if( offset > _reader.size() ) { + _topLeft = _reader.size(); + } else { + _topLeft = offset; + } + // only let _topLeft be an integer multiple of the line length (round down) + _topLeft = (_topLeft/bytesPerLine()) * bytesPerLine(); + // update the labels + // setOffsetLabels(_topLeft); + + const Delta * delta; + + _reader.seek(_topLeft); + _reader.read(_data,bytesPerPage()); + + // update data from delta map + for( offset = _delta.lower_bound(_topLeft); + (offset != -1) && (offset < _topLeft + bytesPerPage()); + offset = _delta.lower_bound(offset) ) { + delta = _delta.search(offset); + _data[offset++-_topLeft] = delta->newData()[0]; + } + + repaint(); + emit topLeftChanged(_topLeft); + } catch( const exception &e ) { + inTopLeft = false; + throw e; + } + inTopLeft = false; +} + +//void HexEditor::setOffsetLabels( off_t topLeft ) +//{ + // need to impliment manually printing labels +//} + +int HexEditor::topMargin() const +{ + return _topMargin; +} +int HexEditor::leftMargin() const +{ + return _leftMargin; +} +// +// access fn's for offset manip +// +int HexEditor::bytesPerPage() const +{ + return _rows*_cols*bytesPerWord(); +} +int HexEditor::bytesPerWord() const +{ + return _bytesPerWord; +} +int HexEditor::bytesPerLine() const +{ + return bytesPerWord()*wordsPerLine(); +} +int HexEditor::wordsPerLine() const +{ + return _cols; +} +int HexEditor::linesPerPage() const +{ + return _rows; +} +int HexEditor::wordsPerPage() const +{ + return _rows*_cols; +} +int HexEditor::charsPerByte() const +{ + return _charsPerByte; +} +int HexEditor::charsPerWord() const +{ + return _charsPerByte*bytesPerWord(); +} +int HexEditor::charsPerLine() const +{ + return _charsPerByte*(bytesPerLine()); +} +// translate local byte offsets to global byte offsets +off_t HexEditor::globalOffset( off_t local ) const +{ + return local+_topLeft; +} +// translate global byte offsets to viewport byte offsets +off_t HexEditor::localOffset( off_t global ) const +{ + return global-_topLeft; +} + +int HexEditor::offsetToPercent( + off_t offset + ) +{ + // round up + return _reader.size() ? (int)ceil(100.0*offset/_reader.size()) : 0; +} + +// public slots: +bool HexEditor::browseLoadFile() +{ + QString filename = QFileDialog::getOpenFileName(); + if( filename.isNull() ) + return false; + return open(filename); +} + +QRect HexEditor::charBBox( off_t charIdx ) const { + // byteIdx = charIdx/charsPerByte + // wordIdx = byteIdx/bytesPerWord + int wordIdx = (charIdx/charsPerByte())/bytesPerWord(); + int localCharIdx = charIdx % charsPerWord(); + return QRect( _wordBBox[wordIdx].left() + localCharIdx*fontMaxWidth() + + wordSpacing()/2, + _wordBBox[wordIdx].top(), + fontMaxWidth(), + fontHeight() ); +} + +QRect HexEditor::byteBBox( off_t byteIdx ) const { + // wordIdx = byteIdx/bytesPerWord + int wordIdx = byteIdx/bytesPerWord(); + int localByteIdx = byteIdx % bytesPerWord(); + return QRect( _wordBBox[wordIdx].left() + localByteIdx*2*fontMaxWidth() + + wordSpacing()/2, + _wordBBox[wordIdx].top(), + fontMaxWidth()*2, + lineSpacing() ); +} + +void HexEditor::setTopLeftToPercent( int percent ) +{ + setTopLeft( (_reader.size()/100)*percent ); +} + +// +// slot for setting cursor offset. +// +void HexEditor::setOffset( off_t offset ) +{ + off_t oldWordOffset = localWordOffset(); + _cursor.setOffset( offset, 0 ); + // updateWord clamps the wordIdx to [0,_rows*_cols) + updateWord( oldWordOffset ); + emit offsetChanged( _cursor.byteOffset() ); +} + +void HexEditor::nextLine() +{ + setTopLeft(_topLeft+bytesPerLine()); +} +void HexEditor::prevLine() +{ + setTopLeft(_topLeft-bytesPerLine()); +} +void HexEditor::nextPage() +{ + setTopLeft(_topLeft+bytesPerPage()); +} +void HexEditor::prevPage() +{ + setTopLeft(_topLeft-bytesPerPage()); +} + +off_t HexEditor::localByteOffsetAtXY(off_t x, off_t y) +{ + off_t wordIdx; + off_t wordLength = wordSpacing()+wordWidth(); + off_t line = min(y/lineSpacing(),(off_t)linesPerPage()); + + // constrain x to be less than the right side of the last char on a line + x = max( (off_t)0, x - leftMargin()); + x = min(wordsPerLine()*wordLength - 1 ,x); + // constrain y to be > topMargin() and less than bottom of last line + y = max( (off_t)0, y - topMargin()); + line = min(y/lineSpacing(), (off_t)linesPerPage()-1); + wordIdx = line*wordsPerLine() + x/wordLength; + + off_t byteOffsetInWord = (x%wordLength)*bytesPerWord()/wordLength; + // = wordIdx*bytesPerWord + byteOffsetInWord + return min( (off_t) bytesPerPage()-1, + wordIdx*bytesPerWord() + byteOffsetInWord); + +} +// +// event handler implimentation: +// +void HexEditor::setCursorFromXY(int x,int y) +{ + off_t oldWordIdx = localWordOffset(); + + _cursor.setOffset( _topLeft+localByteOffsetAtXY(x,y) ,0 ); + + // update where the cursor used to be, and where it is now + if( oldWordIdx != localWordOffset() ) { + updateWord( oldWordIdx ); + } + updateWord( localWordOffset() ); + emit offsetChanged(_cursor.byteOffset()); +} + +void HexEditor::mousePressEvent( QMouseEvent* e ) +{ + setCursorFromXY(e->x(),e->y()); + + off_t byte_offset = localByteOffset(); + QRect bbox = byteBBox(byte_offset); + if( e->x() > bbox.right() ) { + byte_offset++; + } + setSelection( SelectionStart, globalOffset( byte_offset )); +} + +void HexEditor::mouseMoveEvent( QMouseEvent* e ) +{ + setCursorFromXY(e->x(),e->y()); + + off_t byte_offset = localByteOffset(); + QRect bbox = byteBBox(byte_offset); + if(e->x() > bbox.right() ) { + byte_offset++; + } + + setSelection( SelectionEnd, globalOffset( byte_offset )); +} + +void HexEditor::mouseReleaseEvent( QMouseEvent* e ) +{ + setCursorFromXY(e->x(),e->y()); + + off_t byte_offset = localByteOffset(); + QRect bbox = byteBBox(byte_offset); + if(e->x() > bbox.right() ) { + byte_offset++; + } + + setSelection( SelectionEnd, globalOffset( byte_offset )); +} + +off_t HexEditor::selectionStart() const +{ + if( _selection[SelectionStart] == -1 || _selection[SelectionEnd] == -1 ) + return -1; + return min(_selection[SelectionStart],_selection[SelectionEnd]); +} +// note: end is open. range is: [start,end) + +off_t HexEditor::selectionEnd() const +{ + if( _selection[SelectionStart] == -1 || _selection[SelectionEnd] == -1 ) + return -1; + return max(_selection[SelectionStart],_selection[SelectionEnd]); +} + +void HexEditor::setSelection(SelectionPos_e pos, off_t offset) +{ + if( !_reader.is_open() ) { + return; + } + if( pos == SelectionStart ) { + _selection[SelectionEnd] = -1; + } + _selection[pos] = offset; + + if( _selection[SelectionStart] < 0 ) { + emit selectionChanged(""); + } else { + if( selectionEnd() > -1 && selectionEnd() <= _reader.size() ) { + QString data; + const Delta* delta; + for(off_t i = selectionStart(); + i < selectionEnd(); + ++i) { + if( (delta = _delta.search(i)) ) + data += Translate::ByteToHex(delta->newData()[0]); + else + data += Translate::ByteToHex(_reader[i]); + } + emit selectionChanged( data ); + } else { + emit selectionChanged( "" ); + } + } + + repaint(); +} + +// +// Editor implimentation +// +void HexEditor::keyPressEvent( QKeyEvent *e ) +{ + int key = e->key(); + + switch(_base) { + case -1: + // ascii + // range is taken from qnamespace.h + if( key >= Qt::Key_Space && key <= Qt::Key_AsciiTilde ) { + seeCursor(); + vector oldData; + vector newData; + vector chars; + + oldData.push_back( *(_data.begin()+localByteOffset()) ); + Translate::ByteToChar(chars,oldData); + chars[0] = key; + Translate::CharToByte(newData,chars); + _delta.insert( _cursor.byteOffset(), + oldData, + newData); + _data[_cursor.byteOffset()-_topLeft] = newData[0]; + cursorRight(); + setSelection(SelectionStart,-1); + return; + } + break; + case 16: + if ( key >= 'A' && key <= 'F' ) { + key = tolower(key); + } + if ( (key >= 'a' && key <= 'f') || + (key >= '0' && key <= '9') ) { + // + // make sure we can see where the cursor is + // + seeCursor(); + vector oldData; + vector newData; + vector hex; + + oldData.push_back( *(_data.begin()+localByteOffset()) ); + Translate::ByteToHex(hex,oldData); + hex[_cursor.charOffset()] = key; + Translate::HexToByte(newData,hex); + + _delta.insert( _cursor.byteOffset(), + oldData, + newData ); + _data[_cursor.byteOffset()-_topLeft] = newData[0]; + // move to the next char + cursorRight(); + setSelection(SelectionStart,-1); + return; + } + break; + case 8: + if( key >= '0' && key < '8' ) { + // valid octal key + seeCursor(); + vector oldData; + vector newData; + vector octal; + + oldData.push_back( *(_data.begin()+localByteOffset()) ); + Translate::ByteToOctal(octal,oldData); + octal[_cursor.charOffset()] = key; + Translate::OctalToByte(newData,octal); + + _delta.insert( _cursor.byteOffset(), + oldData, + newData ); + _data[_cursor.byteOffset()-_topLeft] = newData[0]; + cursorRight(); + setSelection(SelectionStart,-1); + return; + } + break; + case 2: + if( key >= '0' && key < '2' ) { + // valid binary key + seeCursor(); + vector oldData; + vector newData; + vector binary; + + oldData.push_back( *(_data.begin()+localByteOffset()) ); + Translate::ByteToBinary(binary,oldData); + binary[_cursor.charOffset()] = key; + Translate::BinaryToByte(newData,binary); + + _delta.insert( _cursor.byteOffset(), + oldData, + newData ); + _data[_cursor.byteOffset()-_topLeft] = newData[0]; + cursorRight(); + setSelection(SelectionStart,-1); + return; + } + break; + } + switch ( e->key() ) { + case Qt::Key_Left: + cursorLeft(); + break; + case Qt::Key_Right: + cursorRight(); + break; + case Qt::Key_Up: + cursorUp(); + break; + case Qt::Key_Down: + cursorDown(); + break; + case Qt::Key_PageUp: + prevPage(); + break; + case Qt::Key_PageDown: + nextPage(); + break; + case Qt::Key_End: + setTopLeft( _reader.size() - bytesPerPage()/2 ); + break; + case Qt::Key_Home: + setTopLeft(0); + break; + default: + e->ignore(); + break; + } +} + +void HexEditor::resizeEvent( QResizeEvent * e ) +{ + int height= lineSpacing(); + int totalWordWidth = wordWidth() + wordSpacing(); + int linewidth = e->size().width(); + + // don't let _rows or _cols drop below 1 + _rows = max(1,(e->size().height() - _topMargin)/height); + _cols = max(1,(e->size().width() - _leftMargin)/totalWordWidth); + + // now update the line && word bbox vectors + _lineBBox.reserve(_rows); + _wordBBox.reserve(_rows*_cols); + int top,left; + for(int r = 0; r < _rows; r++) { + top = r*height + _topMargin; + for(int c = 0; c < _cols; c++) { + left = totalWordWidth*c + _leftMargin; + _wordBBox[r*_cols+c] = QRect(left, //left + top, //top + totalWordWidth, //width + height); //height + } + _lineBBox[r] = QRect(_leftMargin,top,linewidth,height); + } + // calculate offset label bounding box + _labelBBox.setRect(0, // x + 0, // y + _leftMargin, // width + e->size().height()); // height + + // do this to recalculate the amount of displayed data. + setTopLeft(_topLeft); + emit rangeChanged(0,_reader.size()/bytesPerLine()); +} +// +// Reimplimented to be more efficient then repainting the whole screen on +// focus events. +// +void HexEditor::focusInEvent( QFocusEvent* ) +{ + updateWord( localWordOffset() ); +} +void HexEditor::focusOutEvent( QFocusEvent* ) +{ + updateWord( localWordOffset() ); +} +// generate's paint events for wordIdx (global wordIdx) +// is safe to call with any wordIdx +void HexEditor::updateWord( off_t wordIdx ) +{ + if( wordIdx > -1 && wordIdx < _rows*_cols ) + repaint(_wordBBox[wordIdx]); +} + +void HexEditor::paintLabels( QPainter* paintPtr) +{ + // ignore redraw range for first aproximation: + int y = _wordBBox[0].bottom(); + unsigned int i; + off_t offset = _topLeft; + uchar *ucptr; + QString label; + + for(int row = 0; row < _rows;++row) { + label = ""; +#ifdef WORDS_BIGENDIAN + for(i=0, ucptr = (uchar*)&offset; idrawText( 5, y, label ); + offset+=bytesPerLine(); + y+=lineSpacing(); + } + // draw dividing line between offset labels and data + int x = leftMargin()-fontMaxWidth(); + paintPtr->drawLine(x,topMargin(), + x,height()-topMargin()); +} +// +// painting optimizations, each time resize is called, it calculates +// the bounding boxes for each word and each line. +// This data can then be retrieved with wordBBox() +// We can then test intersections to see which words need to be redrawn +// +void HexEditor::paintEvent( QPaintEvent* e) +{ +/* QPixmap pixmap(this->width(),this->height()); + pixmap.fill(backgroundRole());*/ + QPainter paint( this );//&pixmap); + + // set up painter;/ + paint.setFont(font()); + const QPalette& p = qApp->palette(); + paint.setBrush(p.background()); + + if( _labelBBox.intersects(e->rect()) ) { + paintLabels(&paint); + } + + QString text; + if( !getDisplayText(text) ) { + cerr << "[error] - internal inconsitency. Please file bug report." + << endl; + return; + } + + // both cursor and selection is drawn underneath the text + drawCursor( paint ); + drawSelection( paint ); + + // Find the stop/start row/col idx's for the repaint + int totalWordWidth = wordWidth()+wordSpacing(); + int row_start = max(0,(e->rect().top()-topMargin())/lineSpacing() ); + int col_start = max(0,(e->rect().left()-leftMargin())/totalWordWidth); + int row_stop = min(_rows-1,e->rect().bottom() / lineSpacing()); + int col_stop = min(_cols-1,e->rect().right() / totalWordWidth); + + // draw text in repaint event + drawTextRegion( paint, text, row_start, row_stop, col_start, col_stop ); +} + +bool HexEditor::getDisplayText( QString& text ) +{ + // get data to draw + switch (_base) { + case 16: + Translate::ByteToHex(text,_data); + break; + case 8: + Translate::ByteToOctal(text,_data); + break; + case 2: + Translate::ByteToBinary(text,_data); + break; + case -1: + Translate::ByteToChar(text,_data); + break; + default: + // error state + return false; + } + return true; +} + +bool HexEditor::wordModified ( off_t wordIdx ) const +{ + off_t lboffset = wordIdx*bytesPerWord()+_topLeft; + off_t nearest_offset = _delta.lower_bound(lboffset); + return ( nearest_offset != -1 && nearest_offset < lboffset+bytesPerWord() ); +} + +// +// accessors for local offsets +// +off_t HexEditor::localByteOffset() const +{ + return _cursor.byteOffset() - _topLeft; +} +off_t HexEditor::localWordOffset() const +{ + return localByteOffset()/bytesPerWord(); +} +// in offset relative to _data[0] +off_t HexEditor::localCharOffset() const +{ + return localByteOffset()*charsPerByte() + _cursor.charOffset(); +} +off_t HexEditor::localLineOffset() const +{ + return localWordOffset()/wordsPerLine(); +} +int HexEditor::wordWidth() const +{ + return _fontMaxWidth*charsPerWord(); +} +int HexEditor::wordSpacing() const +{ + return _wordSpacing; +} +// +// cursor movement members +// +void HexEditor::seeCursor() +{ + // see if it is already visible + if ( _cursor.byteOffset() >= _topLeft && + _cursor.byteOffset() <= _topLeft+bytesPerPage()-1 ) { + updateWord( localWordOffset() ); + return; + } else { + // setTopLeft so cursor is in middle line of screen + setTopLeft( max(_cursor.byteOffset() - bytesPerPage()/2, (off_t)0) ); + } +} + +void HexEditor::cursorLeft() +{ + off_t oldWordIdx = localWordOffset(); + // move the cursor + _cursor.decrByChar(1); + // make sure the user can see the cursor + seeCursor(); + // redraw where the cursor used to be + if( localWordOffset() != oldWordIdx ) + updateWord( oldWordIdx ); + emit offsetChanged( _cursor.byteOffset() ); +} +void HexEditor::cursorRight() +{ + off_t oldWordIdx = localWordOffset(); + _cursor.incrByChar(1); + seeCursor(); + if( localWordOffset() != oldWordIdx ) + updateWord( oldWordIdx ); + emit offsetChanged( _cursor.byteOffset() ); +} +void HexEditor::cursorUp() +{ + off_t oldWordIdx = localWordOffset(); + _cursor.decrByByte( bytesPerLine() ); + seeCursor(); + if( localWordOffset() != oldWordIdx ) + updateWord( oldWordIdx ); + emit offsetChanged( _cursor.byteOffset() ); +} +void HexEditor::cursorDown() +{ + off_t oldWordIdx = localWordOffset(); + _cursor.incrByByte( bytesPerLine() ); + seeCursor(); + if( localWordOffset() != oldWordIdx ) + updateWord( oldWordIdx ); + emit offsetChanged( _cursor.byteOffset() ); +} + +// slots for undo/redo. +// note: it is necessary to reset topLeft to force a reread of the data. +// +void HexEditor::redo() +{ + _delta.redo(); + setTopLeft(_topLeft); + off_t start = selectionStart(); + off_t end = selectionEnd(); + setSelection(SelectionStart,start); + setSelection(SelectionEnd,end); +} + +void HexEditor::undo() +{ + _delta.undo(); + setTopLeft(_topLeft); + off_t start = selectionStart(); + off_t end = selectionEnd(); + setSelection(SelectionStart,start); + setSelection(SelectionEnd,end); +} + +void HexEditor::search( const QString& hexText, bool forwards ) +{ + QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); + if( !hexText.length() || _reader.filename() == "" ) + return; + + if( -1 != _delta.lower_bound(0) ) { + QMessageBox::information(this,PROGRAM_STRING, + "Cannot search file with unsaved modifications."); + return; + } + + vector data; + Translate::HexToByte(data,hexText); +// QProgressDialog progress( "Searching...","Cancel", 0, +// _reader.size(),this ); +// progress.setWindowModality( Qt::WindowModal ); +// progress.setWindowTitle(PROGRAM_STRING); +// progress.setValue(0); + off_t pos; + if( forwards ) { + pos = _reader.findIndex(_cursor.byteOffset(),data,_reader.size()-1); + } else { + pos = _reader.rFindIndex(_cursor.byteOffset(),data,0); + } + if( pos < _reader.size() ) { + showMatch(pos,data.size()); + setOffset( forwards?selectionEnd():selectionStart()-1 ); + seeCursor(); + } else { + QMessageBox::information(this,PROGRAM_STRING, + "Could not find search data 0x" + hexText); + } + QApplication::restoreOverrideCursor(); +} +void HexEditor::setBaseASCII() { + setBase(-1); +} +void HexEditor::setBaseHex() +{ + setBase(16); +} +void HexEditor::setBaseOctal() +{ + setBase(8); +} +void HexEditor::setBaseBinary() +{ + setBase(2); +} +void HexEditor::setBase(int base) +{ + switch(base) { + case -1: + // setup ascii editing mode + _charsPerByte = 1; + break; + case 2: + // setup binary editing mode + _charsPerByte = 8; + break; + case 8: + // setup octal editing mode + _charsPerByte = 3; + break; + case 16: + // setup hex editing mode + _charsPerByte = 2; + break; + default: + // just ignore unsupported bases for now + return; + } + _base = base; + _cursor.setCharsPerByte(_charsPerByte); + // refresh the display + // note: cannot call resize() because it will ignore resize events + // if the size has not changed. + QResizeEvent *re = new QResizeEvent(QSize(size()),QSize(size())); + resizeEvent(re); + delete re; + // make sure we can still see the cursor + // switching from a larger base to a smaller base has the + // possibility of pushing the cursor off the screen + seeCursor(); +} + +off_t HexEditor::offset() const +{ + return _cursor.byteOffset(); +} + +Reader * HexEditor::reader() +{ + return &_reader; +} + +void HexEditor::showMatch( off_t pos, int len ) +{ + setSelection( SelectionStart, pos ); + setSelection( SelectionEnd, pos + len ); +} + +void HexEditor::drawTextRegion( QPainter& paint, const QString& text, + int row_start, int row_stop, + int col_start, int col_stop ) +{ + paint.setPen(qApp->palette().foreground().color()); + for(int r = row_start; r <= row_stop; r++) { + for(int c = col_start; c <= col_stop; c++) { + int widx = r*_cols+c; + if ( wordModified( widx ) ) { + paint.setPen(qApp->palette().link().color()); + paint.drawText( _wordBBox[widx].left() + wordSpacing()/2, + _wordBBox[widx].bottom(), + text.mid(widx*charsPerWord(),charsPerWord()) ); + paint.setPen(qApp->palette().foreground().color()); + } else { + paint.drawText( _wordBBox[widx].left() + wordSpacing()/2, + _wordBBox[widx].bottom(), + text.mid(widx*charsPerWord(),charsPerWord()) ); + } + } + } +} + +void HexEditor::drawSelection( QPainter& paint ) +{ + // draw selection + off_t start = max( (off_t)0, selectionStart() - _topLeft); + if( start < bytesPerPage() ) { + off_t stop = min(selectionEnd() - _topLeft, (off_t)bytesPerPage()); + paint.setPen(Qt::NoPen); + paint.setBrush( qApp->palette().highlight() ); + // foreach line with selection + stop--; + while( start <= stop ) { + // linestop = min(stop,endofline) + off_t linestop = + min(stop, start+bytesPerLine()-1 -(start % bytesPerLine())); + QRect bbox = byteBBox(start); + bbox.setRight( byteBBox(linestop).right() ); + paint.drawRect( bbox ); + start = linestop+1; + } + } +} + +void HexEditor::drawCursor( QPainter& paint ) +{ + QBrush b = qApp->palette().mid(); + if( localWordOffset() > -1 && localWordOffset() < wordsPerPage() ) { + if( hasFocus() ) { + // draw a solid cursor + paint.setPen(Qt::NoPen); + paint.setBrush(b); + } else { + // just draw the outline + paint.setPen(b.color()); + paint.setBrush(Qt::NoBrush); + } + QRect box = charBBox( localCharOffset() ); + paint.drawRect( box ); + } +} diff --git a/hexEditor.hpp b/hexEditor.hpp new file mode 100755 index 0000000..2cd58b7 --- /dev/null +++ b/hexEditor.hpp @@ -0,0 +1,242 @@ +/* $Id: hexEditor.hpp,v 1.7 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _HEX_EDITOR_ +#define _HEX_EDITOR_ + +#include +#include +#include +#include +#include +#include + +// STL headers +#include + +// System headers +#include + +// Local headers +#include "local.h" +#include "reader.hpp" +#include "delta.hpp" +#include "cursor.hpp" +#include "save.hpp" + +class HexEditor : public QWidget { + Q_OBJECT +public: + HexEditor( QWidget * parent =0 ); + ~HexEditor(); + + // note: closeFile returns QMessageBox::{Yes,No,Cancel} + int closeFile(bool force = false ); + bool open(const QString& filename); + // becomes "save as" if filename != "" + bool save(QString filename = ""); + bool isModified() const; + QString filename() const; + Reader * reader(); + off_t offset() const; + + + // returns the word (string representation) that the cursor is in + // QString currentWord() const; + // + // cursor movement related fn's + // + void seeCursor(); + void cursorLeft(); + void cursorRight(); + void cursorUp(); + void cursorDown(); + int wordWidth() const; + int wordSpacing() const; + void setCursorFromXY(int x,int y); + +public: + enum SelectionPos_e { + SelectionStart, + SelectionEnd, + }; + void setSelection(SelectionPos_e pos,off_t byte_offset); + off_t selectionStart() const; + off_t selectionEnd() const; + // get the char offset from topleft of the point (x,y) + // Note: this wont work for large files, off_t is 2*size() at eof for hex + //off_t charOffsetAtXY( off_t x, off_t y ); + int offsetToPercent( off_t offset ); + + off_t localByteOffsetAtXY( off_t x, off_t y ); +protected: + // accessors for local offset's + // note: returned offset's are not necessarily visible + off_t localByteOffset() const; + off_t localWordOffset() const; + off_t localCharOffset() const; + off_t localLineOffset() const; + +signals: + // attach to know when line ranges change (for a scrollbar) + void rangeChanged(off_t low, off_t high); + void offsetChanged(off_t byte); + void topLeftChanged(off_t offset); + void selectionChanged(const QString& selection); + +public slots: + bool browseLoadFile(); + void setOffset(off_t offset); // sets cursor offset + void setTopLeftToPercent( int percent ); // for setting pos from scroll + // + // slots for setting the number of bytes per column + // I really should change all references of "bytesPerWord" to "bytesPerCol" + // sorry for the confusion -Salem + // + void setBytesPerWord( int i ); + void set1BPC() { setBytesPerWord(1); }; + void set2BPC() { setBytesPerWord(2); }; + void set4BPC() { setBytesPerWord(4); }; + void set8BPC() { setBytesPerWord(8); }; + void nextLine(); + void prevLine(); + void nextPage(); + void prevPage(); + void setFont(const QFont& font); + void undo(); + void redo(); + void search(const QString& hexText, bool forwards); + void setBase(int base); // only 2 8 16 acceptableO + void setBaseHex(); + void setBaseOctal(); + void setBaseBinary(); + void setBaseASCII(); + +protected: + // void setOffsetLabels( off_t topLeft ); + void setTopLeft( off_t offset ); + +protected: + bool wordModified( off_t widx ) const; + // template setWord so I can call it with different types of string data + template + void setWord( off_t wordIdx, const _vectType& str ); + // calculate the rectangle which bounds the word at _offset+idx + QRect charBBox( off_t charIdx ) const; + QRect byteBBox( off_t byteIdx ) const; + + // translate widget coord to word index + int pointToWord(const QPoint& pt); + QChar& pointToChar(const QPoint& pt); + // inlined access fn's +protected: + void calculateFontMetrics(); + int charsPerByte() const; + int charsPerWord() const; + int charsPerLine() const; + int bytesPerPage() const; + int bytesPerWord() const; + int bytesPerLine() const; + int wordsPerLine() const; + int wordsPerPage() const; + int linesPerPage() const; + + int lineSpacing() const; + int fontHeight() const; + int fontMaxWidth() const; + int topMargin() const; + int leftMargin() const; + + // translate local offset to global offset + off_t globalOffset( off_t local ) const; + // translate global offset to local offset. + // Note: globalOffset may not exist in local viewport. + off_t localOffset ( off_t global) const; + +protected: + void showMatch( off_t pos, int len ); + // drawing utilities +protected: + bool getDisplayText( QString& text ); + void drawCursor( QPainter& p ); + void drawSelection( QPainter& p ); + void drawTextRegion( QPainter& p, const QString& text, + int row_start, int row_end, + int col_start, int col_end ); + // event handlers +protected: + void resizeEvent ( QResizeEvent *e ); + // generates update() events for the bbox for the word given byh wordIdx + // note: this is range safe, no need to do range checking before passing in + void updateWord ( off_t wordIdx ); + void paintEvent ( QPaintEvent*e ); + void paintLabels ( QPainter*painter); + void focusInEvent ( QFocusEvent*e ); + void focusOutEvent ( QFocusEvent*e ); + void keyPressEvent ( QKeyEvent *e ); + void mousePressEvent ( QMouseEvent * e ); + void mouseReleaseEvent( QMouseEvent *e ); + void mouseMoveEvent ( QMouseEvent *e ); + +protected: + Reader _reader; + + int _wordSpacing; + int _wordWidth; + int _lineSpacing; + int _fontHeight; + int _bytesPerWord; + int _charsPerByte; + off_t _topLeft; + + // + // current screen data info + // + vector _data; + int _base; // either 2 8 or 16 + + off_t _lastValidWord; + + // data used to optimize drawing. + vector _lineBBox; + vector _wordBBox; + QRect _labelBBox; + + int _linspacing; + int _fontMaxWidth; + + int _topMargin,_leftMargin; + int _offsetLabelBytes; + + int _cols; + int _rows; + int _width; + Cursor _cursor; + + // + // when refering to the offset of a word, I always mean the offset of the + // first byte of the word + // + + // this class keeps track of modifications + DeltaMap _delta; + + // selection: + off_t _selection[2]; +}; + +#endif diff --git a/hexGui.cpp b/hexGui.cpp new file mode 100644 index 0000000..f429cee --- /dev/null +++ b/hexGui.cpp @@ -0,0 +1,326 @@ +/* $Id: hexGui.cpp,v 1.13 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hexGui.hpp" +#include "box.hpp" +#include "expr.h" + +HexGui::HexGui (QWidget * p) + : QMainWindow(p) +{ + QWidget* h = new QWidget(this); + QHBoxLayout* l = new QHBoxLayout(h); + + hexEditor = new HexEditor(h); + hexEditor->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, + QSizePolicy::Expanding ) ); + l->addWidget(hexEditor); + vsb = new QScrollBar(h); + l->addWidget(vsb); + vsb->setRange(0,0); + + setCentralWidget(h); + + connect( hexEditor,SIGNAL(rangeChanged(off_t,off_t)), + this,SLOT(setScrollBarRange(off_t,off_t)) ); + connect( vsb, SIGNAL(valueChanged(int)), + hexEditor,SLOT(setTopLeftToPercent(int)) ); + connect( hexEditor, SIGNAL(topLeftChanged(off_t)), + this, SLOT(setScrollBarValue(off_t)) ); + connect( hexEditor, SIGNAL(offsetChanged(off_t)), + this, SLOT(setOffsetLabel(off_t)) ); + + // setup dock + QDockWidget* dock = new QDockWidget("Conversions Assistant",this); + vbox* vb = new vbox(dock); + conversionDlg = new ConversionDlg(vb); + connect(conversionDlg,SIGNAL(nextPressed()),this,SLOT(searchForwards())); + connect(conversionDlg,SIGNAL(prevPressed()),this,SLOT(searchBackwards())); + + dock->setWidget(vb); + dock->hide(); + connect(hexEditor,SIGNAL(selectionChanged(const QString &)), + conversionDlg, SLOT(valueChanged(const QString&)) ); + addDockWidget( Qt::RightDockWidgetArea, dock ); + dock->setFloating(true); + + // setup menu: + fileMenu = new QMenu("&File",this); + fileMenu->addAction("&Open...",this,SLOT(browseLoadFile())); + fileMenu->addAction("&Open in new editor...",this, + SLOT(browseLoadFileNew())); + fileMenu->addSeparator(); + actions[SaveAction] = fileMenu->addAction("&Save",this,SLOT(save())); + actions[SaveAsAction] = fileMenu->addAction("Save &As...",this, + SLOT(saveAs())); + // only include close functions in menu if this is a toplevel window + if( isTopLevel() ) { + fileMenu->addSeparator(); + if( isTopLevel() ) + fileMenu->addAction("Close Editor",this,SLOT(close())); + else + fileMenu->addAction("Close File",this,SLOT(closeFile())); + fileMenu->addAction("&Exit Application",qApp, SLOT(quit())); + } + menuBar()->addMenu( fileMenu ); + + // edit pulldown + editMenu = new QMenu( "&Edit", this ); + actions[UndoAction] = editMenu->addAction("&Undo",hexEditor,SLOT(undo())); + actions[RedoAction] = editMenu->addAction("&Redo",hexEditor,SLOT(redo())); + menuBar()->addMenu( editMenu ); + + // view menu + viewMenu = new QMenu("&View",this); + QAction* dockAction = dock->toggleViewAction(); + viewMenu->addAction( dockAction ); + + // menu for selecting editing base (octal/binary/hex) + QMenu * baseMenu = new QMenu("&Editing base", this); + baseMenu->addAction("&Hex",hexEditor,SLOT(setBaseHex())); + baseMenu->addAction("&ASCII",hexEditor,SLOT(setBaseASCII())); + baseMenu->addAction("&Binary",hexEditor,SLOT(setBaseBinary())); + baseMenu->addAction("&Octal",hexEditor,SLOT(setBaseOctal())); + viewMenu->addMenu(baseMenu); + // menu for selecting bytes per column + QMenu * nbytesMenu = new QMenu("&Bytes per column",this); + nbytesMenu->addAction("&1",hexEditor,SLOT(set1BPC())); + nbytesMenu->addAction("&2",hexEditor,SLOT(set2BPC())); + nbytesMenu->addAction("&4",hexEditor,SLOT(set4BPC())); + nbytesMenu->addAction("&8",hexEditor,SLOT(set8BPC())); + viewMenu->addMenu(nbytesMenu); + menuBar()->addMenu(viewMenu); + + resize(400,196); + statusBar()->showMessage("Ready",2000); + statusBar()->addWidget(new QLabel("Cursor Offset:",statusBar())); + offsetLineEdit = new QLineEdit(statusBar()); +// offsetLineEdit->setValidator( new HexValidator(offsetLineEdit) ); + connect(offsetLineEdit,SIGNAL(returnPressed()), + SLOT(setOffsetFromStatusBar())); + statusBar()->addWidget(offsetLineEdit); + // progressBar = new QProgressBar(statusBar()); + // statusBar()->addWidget(progressBar,1,FALSE); + statusLabel = new QLabel("",statusBar()); + statusBar()->addWidget(statusLabel,1); + statusBar()->setSizeGripEnabled( !parent() ); + setWindowTitle(PROGRAM_STRING); + + // deactivate save,saveas undo and redo + actions[SaveAction]->setEnabled(false); + actions[SaveAsAction]->setEnabled(false); + actions[UndoAction]->setEnabled(false); + actions[RedoAction]->setEnabled(false); +} + +HexGui::~HexGui() +{ +} + +bool HexGui::open(const char * filename) +{ + if (hexEditor->open(filename)) { + if( isTopLevel() ) + setWindowTitle(filename); + else + statusLabel->setText(filename); + actions[SaveAction]->setEnabled(true); + actions[SaveAsAction]->setEnabled(true); + actions[UndoAction]->setEnabled(true); + actions[RedoAction]->setEnabled(true); + return true; + } else { + return false; + } +} + +void HexGui::gotoOffset(off_t offset) +{ + hexEditor->setOffset(offset); + hexEditor->seeCursor(); + emit offsetChanged(offset); +} + +bool HexGui::browseLoadFile() +{ + QString filename = QFileDialog::getOpenFileName(); + if( filename.isNull() ) + return false; + return open( C_STR(filename) ); +} +bool HexGui::browseLoadFileNew() +{ + QString filename = QFileDialog::getOpenFileName(); + if( filename.isNull() ) + return false; + HexGui * hg = new HexGui(); + bool retval = hg->open( C_STR(filename) ); + hg->show(); + return retval; +} + +bool HexGui::save() +{ + statusBar()->showMessage("saveing " + hexEditor->filename()) ; + bool retval = hexEditor->save(); + statusBar()->showMessage("done.",2000); + return retval; +} + +bool HexGui::saveAs() +{ + QString filename = QFileDialog::getSaveFileName(); + if( filename.isNull() ) + return false; + statusBar()->showMessage("saveing to \"" + filename + "\"..."); + bool retval = hexEditor->save(filename); + statusBar()->showMessage("done.",2000); + setWindowTitle(filename); + return retval; +} + +int HexGui::closeFile() +{ + return closeFile( false ); +} +int HexGui::closeFile( bool force ) +{ + return hexEditor->closeFile(force); +} + +void HexGui::closeEvent( QCloseEvent * ce ) +{ + if( hexEditor->closeFile() == QMessageBox::Cancel ) + ce->ignore(); + else + ce->accept(); +} + +void HexGui::setScrollBarRange(off_t low, off_t high) +{ + (void)low;(void)high; + // range must be contained in the space of an integer, just do 100 + // increments + vsb->setRange(0,100); +} +void HexGui::setScrollBarValue(off_t pos) +{ + // pos is the topLeft pos, set the scrollbar to the + // location of the last byte on the page + // Note: offsetToPercent now rounds up, so we don't + // have to worry about if this is the topLeft or bottom right + vsb->setValue(hexEditor->offsetToPercent(pos)); +} +void HexGui::setOffsetLabel(off_t pos) +{ + QString label; + char buffer[64]; +#if _LARGEFILE_SOURCE + sprintf(buffer,"0x%llx",pos); +#else + sprintf(buffer,"0x%lx",pos); +#endif + label = buffer; + offsetLineEdit->setText(label); +} + +const QString& HexGui::filename(void) const +{ + static QString value = ""; + if(!hexEditor) + return (value=""); + return (value=hexEditor->filename()); +} + +void HexGui::setOffsetFromStatusBar() +{ + // remove leading "0x" if necessary + off_t o; + if( !expr_eval( C_STR(offsetLineEdit->text()),o ) || o < 0 ) { + QApplication::beep(); + } else { + gotoOffset(o); + } + return; + bool ok; + off_t offset; + QString value = offsetLineEdit->text(); + if( value.length() > 2 && value[1] == 'x' ) { + value = value.mid(2); + offset = value.toInt(&ok,16); + } else { + offset = value.toInt(&ok,10); + if( !ok ) { + offset = value.toInt(&ok,16); + } + } + if( !ok ) { + cerr << "Error converting offset: " << C_STR(offsetLineEdit->text()) + << endl; + } else { + gotoOffset(offset); + } +} + +off_t HexGui::offset() const +{ + return hexEditor->offset(); +} + +Reader * HexGui::reader() +{ + return hexEditor->reader(); +} + +void HexGui::setSelection(off_t start,off_t stop) +{ + hexEditor->setSelection(HexEditor::SelectionStart,start); + hexEditor->setSelection(HexEditor::SelectionEnd,stop); +} + +void HexGui::search( bool forward ) +{ + QString hex = conversionDlg->hexValue(); + if( !hex.isEmpty() ) { + hexEditor->search( hex, forward ); + } +} + +void HexGui::searchForwards() +{ + search(true); +} + +void HexGui::searchBackwards() +{ + search(false); +} diff --git a/hexGui.hpp b/hexGui.hpp new file mode 100644 index 0000000..38b71f0 --- /dev/null +++ b/hexGui.hpp @@ -0,0 +1,97 @@ +/* $Id: hexGui.hpp,v 1.5 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _GUI_HPP_ +#define _GUI_HPP_ + +#include +#include +#include + +#include "hexEditor.hpp" +#include "conversionDlg.hpp" +#include "local.h" + +class QScrollBar; +class QMenu; +class QString; +class QLabel; +//class QProgressBar; + +class HexGui : public QMainWindow { + Q_OBJECT +public: + HexGui( QWidget * parent = 0 ); + ~HexGui(); + + const QString& filename(void) const; + // see through call to isModified in hexEditor + bool isModified() const {return hexEditor->isModified();} + // public access functions + off_t offset() const; + Reader * reader(); + +signals: + void offsetChanged(off_t offset); + +public slots: +// sets selection to [start,stop) + void setSelection(off_t start, off_t stop); + bool browseLoadFile(); + bool browseLoadFileNew(); + bool open(const char * filename); + void gotoOffset(off_t offset); + void setOffsetFromStatusBar(void); + + bool save(); + bool saveAs(); + void setScrollBarRange(off_t low, off_t high); + void setScrollBarValue(off_t pos); + void setOffsetLabel(off_t pos); + void closeEvent(QCloseEvent *ce); + // had to trivially overload closeFile() == closeFile(false) for Qt slots + int closeFile(); + int closeFile(bool force); + void searchForwards(); + void searchBackwards(); + +protected: + void search( bool forwards ); + +protected: + enum ActionID_e { + UndoAction, + RedoAction, + SaveAction, + SaveAsAction, + TotalActions + }; + + QLabel *statusLabel; + QLineEdit *offsetLineEdit; + // QProgressBar *progressBar; + HexEditor *hexEditor; + ConversionDlg *conversionDlg; + QScrollBar *vsb; + QAction* actions[TotalActions]; + QMenu *fileMenu; + QMenu *editMenu; + QMenu *viewMenu; +}; + +#endif diff --git a/images.qrc b/images.qrc new file mode 100644 index 0000000..6304052 --- /dev/null +++ b/images.qrc @@ -0,0 +1,9 @@ + + + img/exit.xbm + img/first.xbm + img/last.xbm + img/next.xbm + img/prev.xbm + + \ No newline at end of file diff --git a/img/exit.xbm b/img/exit.xbm new file mode 100644 index 0000000..40c6979 --- /dev/null +++ b/img/exit.xbm @@ -0,0 +1,6 @@ +#define exit_width 16 +#define exit_height 16 +static unsigned char exit_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x38, 0x38, 0x70, 0x3c, 0xc0, 0x1e, 0x80, 0x07, + 0xc0, 0x03, 0xe0, 0x06, 0x70, 0x0c, 0x78, 0x1c, 0x3c, 0x1c, 0x3c, 0x18, + 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/img/first.xbm b/img/first.xbm new file mode 100644 index 0000000..081acc8 --- /dev/null +++ b/img/first.xbm @@ -0,0 +1,6 @@ +#define first_width 16 +#define first_height 16 +static unsigned char first_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x60, 0x60, 0x70, 0x70, 0x78, 0x78, + 0x7c, 0x7c, 0x7e, 0x7e, 0x7e, 0x7e, 0x7c, 0x7c, 0x78, 0x78, 0x70, 0x70, + 0x60, 0x60, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00}; diff --git a/img/last.xbm b/img/last.xbm new file mode 100644 index 0000000..39907a8 --- /dev/null +++ b/img/last.xbm @@ -0,0 +1,6 @@ +#define last_width 16 +#define last_height 16 +static unsigned char last_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x06, 0x06, 0x0e, 0x0e, 0x1e, 0x1e, + 0x3e, 0x3e, 0x7e, 0x7e, 0x7e, 0x7e, 0x3e, 0x3e, 0x1e, 0x1e, 0x0e, 0x0e, + 0x06, 0x06, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00}; diff --git a/img/next.xbm b/img/next.xbm new file mode 100644 index 0000000..b7b5646 --- /dev/null +++ b/img/next.xbm @@ -0,0 +1,6 @@ +#define next_width 16 +#define next_height 16 +static unsigned char next_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x60, 0x00, 0xe0, 0x00, 0xe0, 0x01, + 0xe0, 0x03, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x03, 0xe0, 0x01, 0xe0, 0x00, + 0x60, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/img/prev.xbm b/img/prev.xbm new file mode 100644 index 0000000..5cb72fc --- /dev/null +++ b/img/prev.xbm @@ -0,0 +1,6 @@ +#define prev_width 16 +#define prev_height 16 +static unsigned char prev_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x06, 0x00, 0x07, 0x80, 0x07, + 0xc0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0x80, 0x07, 0x00, 0x07, + 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00}; diff --git a/lfhex.pro b/lfhex.pro new file mode 100644 index 0000000..d67004e --- /dev/null +++ b/lfhex.pro @@ -0,0 +1,36 @@ +# $Id: lfhex.pro,v 1.1 2006/11/05 03:22:13 ganzhorn Exp $ +TARGET = lfhex +SOURCES = reader.cpp \ + save.cpp \ + translate.cpp \ + hexEditor.cpp \ + hexGui.cpp \ + grid.cpp \ + compareDlg.cpp \ + conversionDlg.cpp \ + cursor.cpp \ + delta.cpp \ + offsetConstraint.cpp \ + driver.cpp +HEADERS = offsetConstraint.hpp \ + hexGui.hpp \ + hexEditor.hpp \ + conversionDlg.hpp \ + compareDlg.hpp + +# The following defines are used to select large file offsets. +# If your OS does not support this comment it out. +DEFINES += _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64 +# If your machine is big endian then uncomment the following, otherwise the +# offset labels will be in little endian (which some find hard to read). +# DEFINES += WORDS_BIGENDIAN +lfhex.path = /usr/local/bin +lfhex.files = lfhex +INSTALLS += lfhex +RESOURCES = images.qrc +YACCSOURCES += expr.y +LEXSOURCES += expr.l +QMAKE_LEX = flex +QMAKE_YACC = bison +QMAKE_YACCFLAGS = -d -b expr -p expr + diff --git a/local.h b/local.h new file mode 100644 index 0000000..53dbb1a --- /dev/null +++ b/local.h @@ -0,0 +1,30 @@ +#ifndef _LOCAL_H_ +#define _LOCAL_H_ +/* $Id: local.h,v 1.5 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +typedef unsigned char uchar; + +using namespace std; + +#define C_STR(x) x.toAscii().constData() +#define PROGRAM "lfhex" +#define VERSION "0.4" +#define PROGRAM_STRING "lfhex 0.4" + +#endif diff --git a/mappings.h b/mappings.h new file mode 100755 index 0000000..5d9da1e --- /dev/null +++ b/mappings.h @@ -0,0 +1,179 @@ +/* $Id: mappings.h,v 1.3 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* automatically generated mappings from gen*To*Map.pl scripts */ + +namespace TranslationTables { + char * byteToBinaryMap[256] = { + "00000000", "00000001", "00000010", "00000011", "00000100", "00000101", + "00000110", "00000111", "00001000", "00001001", "00001010", "00001011", + "00001100", "00001101", "00001110", "00001111", "00010000", "00010001", + "00010010", "00010011", "00010100", "00010101", "00010110", "00010111", + "00011000", "00011001", "00011010", "00011011", "00011100", "00011101", + "00011110", "00011111", "00100000", "00100001", "00100010", "00100011", + "00100100", "00100101", "00100110", "00100111", "00101000", "00101001", + "00101010", "00101011", "00101100", "00101101", "00101110", "00101111", + "00110000", "00110001", "00110010", "00110011", "00110100", "00110101", + "00110110", "00110111", "00111000", "00111001", "00111010", "00111011", + "00111100", "00111101", "00111110", "00111111", "01000000", "01000001", + "01000010", "01000011", "01000100", "01000101", "01000110", "01000111", + "01001000", "01001001", "01001010", "01001011", "01001100", "01001101", + "01001110", "01001111", "01010000", "01010001", "01010010", "01010011", + "01010100", "01010101", "01010110", "01010111", "01011000", "01011001", + "01011010", "01011011", "01011100", "01011101", "01011110", "01011111", + "01100000", "01100001", "01100010", "01100011", "01100100", "01100101", + "01100110", "01100111", "01101000", "01101001", "01101010", "01101011", + "01101100", "01101101", "01101110", "01101111", "01110000", "01110001", + "01110010", "01110011", "01110100", "01110101", "01110110", "01110111", + "01111000", "01111001", "01111010", "01111011", "01111100", "01111101", + "01111110", "01111111", "10000000", "10000001", "10000010", "10000011", + "10000100", "10000101", "10000110", "10000111", "10001000", "10001001", + "10001010", "10001011", "10001100", "10001101", "10001110", "10001111", + "10010000", "10010001", "10010010", "10010011", "10010100", "10010101", + "10010110", "10010111", "10011000", "10011001", "10011010", "10011011", + "10011100", "10011101", "10011110", "10011111", "10100000", "10100001", + "10100010", "10100011", "10100100", "10100101", "10100110", "10100111", + "10101000", "10101001", "10101010", "10101011", "10101100", "10101101", + "10101110", "10101111", "10110000", "10110001", "10110010", "10110011", + "10110100", "10110101", "10110110", "10110111", "10111000", "10111001", + "10111010", "10111011", "10111100", "10111101", "10111110", "10111111", + "11000000", "11000001", "11000010", "11000011", "11000100", "11000101", + "11000110", "11000111", "11001000", "11001001", "11001010", "11001011", + "11001100", "11001101", "11001110", "11001111", "11010000", "11010001", + "11010010", "11010011", "11010100", "11010101", "11010110", "11010111", + "11011000", "11011001", "11011010", "11011011", "11011100", "11011101", + "11011110", "11011111", "11100000", "11100001", "11100010", "11100011", + "11100100", "11100101", "11100110", "11100111", "11101000", "11101001", + "11101010", "11101011", "11101100", "11101101", "11101110", "11101111", + "11110000", "11110001", "11110010", "11110011", "11110100", "11110101", + "11110110", "11110111", "11111000", "11111001", "11111010", "11111011", + "11111100", "11111101", "11111110", "11111111", + }; + char byteToCharMap[256] = { + '.','.','.','.','.','.','.','.','.','.', + '.','.','.','.','.','.','.','.','.','.', + '.','.','.','.','.','.','.','.','.','.', + '.','.',' ','!','"','#','.','%','&','\'', + '(',')','*','+',',','-','.','/','0','1', + '2','3','4','5','6','7','8','9',':',';', + '<','=','>','?','@','A','B','C','D','E', + 'F','G','H','I','J','K','L','M','N','O', + 'P','Q','R','S','T','U','V','W','X','Y', + 'Z','[','\\',']','^','_','`','a','b','c', + 'd','e','f','g','h','i','j','k','l','m', + 'n','o','p','q','r','s','t','u','v','w', + 'x','y','z','{','|','}','~','.','.','.', + '.','.','.','.','.','.','.','.','.','.', + '.','.','.','.','.','.','.','.','.','.', + '.','.','.','.','.','.','.','.','.','.', + '.','.','.','.','.','.','.','.','.','.', + '.','.','.','.','.','.','.','.','.','.', + '.','.','.','.','.','.','.','.','.','.', + '.','.','.','.','.','.','.','.','.','.', + '.','.','.','.','.','.','.','.','.','.', + '.','.','.','.','.','.','.','.','.','.', + '.','.','.','.','.','.','.','.','.','.', + '.','.','.','.','.','.','.','.','.','.', + '.','.','.','.','.','.','.','.','.','.', + '.','.','.','.','.','.', + }; + char * byteToHexMap[256] = { + "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", + "0a", "0b", "0c", "0d", "0e", "0f", "10", "11", "12", "13", + "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", + "1e", "1f", "20", "21", "22", "23", "24", "25", "26", "27", + "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", "30", "31", + "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", + "3c", "3d", "3e", "3f", "40", "41", "42", "43", "44", "45", + "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", + "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", + "5a", "5b", "5c", "5d", "5e", "5f", "60", "61", "62", "63", + "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", + "6e", "6f", "70", "71", "72", "73", "74", "75", "76", "77", + "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", "80", "81", + "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", + "8c", "8d", "8e", "8f", "90", "91", "92", "93", "94", "95", + "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", + "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", + "aa", "ab", "ac", "ad", "ae", "af", "b0", "b1", "b2", "b3", + "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", + "be", "bf", "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", + "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", "d0", "d1", + "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", + "dc", "dd", "de", "df", "e0", "e1", "e2", "e3", "e4", "e5", + "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", + "fa", "fb", "fc", "fd", "fe", "ff", + }; + char * byteToOctalMap[256] = { + "000", "001", "002", "003", "004", "005", "006", "007", "010", "011", + "012", "013", "014", "015", "016", "017", "020", "021", "022", "023", + "024", "025", "026", "027", "030", "031", "032", "033", "034", "035", + "036", "037", "040", "041", "042", "043", "044", "045", "046", "047", + "050", "051", "052", "053", "054", "055", "056", "057", "060", "061", + "062", "063", "064", "065", "066", "067", "070", "071", "072", "073", + "074", "075", "076", "077", "100", "101", "102", "103", "104", "105", + "106", "107", "110", "111", "112", "113", "114", "115", "116", "117", + "120", "121", "122", "123", "124", "125", "126", "127", "130", "131", + "132", "133", "134", "135", "136", "137", "140", "141", "142", "143", + "144", "145", "146", "147", "150", "151", "152", "153", "154", "155", + "156", "157", "160", "161", "162", "163", "164", "165", "166", "167", + "170", "171", "172", "173", "174", "175", "176", "177", "200", "201", + "202", "203", "204", "205", "206", "207", "210", "211", "212", "213", + "214", "215", "216", "217", "220", "221", "222", "223", "224", "225", + "226", "227", "230", "231", "232", "233", "234", "235", "236", "237", + "240", "241", "242", "243", "244", "245", "246", "247", "250", "251", + "252", "253", "254", "255", "256", "257", "260", "261", "262", "263", + "264", "265", "266", "267", "270", "271", "272", "273", "274", "275", + "276", "277", "300", "301", "302", "303", "304", "305", "306", "307", + "310", "311", "312", "313", "314", "315", "316", "317", "320", "321", + "322", "323", "324", "325", "326", "327", "330", "331", "332", "333", + "334", "335", "336", "337", "340", "341", "342", "343", "344", "345", + "346", "347", "350", "351", "352", "353", "354", "355", "356", "357", + "360", "361", "362", "363", "364", "365", "366", "367", "370", "371", + "372", "373", "374", "375", "376", "377", + }; + unsigned char hexToByteMap[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, + 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, + 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, + 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + }; +}; diff --git a/offsetConstraint.cpp b/offsetConstraint.cpp new file mode 100644 index 0000000..3369bf9 --- /dev/null +++ b/offsetConstraint.cpp @@ -0,0 +1,98 @@ +/* $Id: offsetConstraint.cpp,v 1.7 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include // for min<>() + +#include "local.h" +#include "offsetConstraint.hpp" +#include "box.hpp" + +OffsetConstraint::OffsetConstraint( QWidget * parent ) + : QGroupBox(parent) +{ + QLayout* l = new QHBoxLayout(this); + this->setLayout(l); + hbox * h = new hbox(this); + l->addWidget(h); + new QLabel("X =",h); + stride = new QSpinBox(h); + stride->setRange(1,16); + stride->setValue(1); + connect(stride,SIGNAL(valueChanged(int)), + this,SLOT(setStride(int))); + + h = new hbox(this); + l->addWidget(h); + new QLabel("Y =",h); + offset = new QSpinBox(h); + offset->setRange(1,16); + offset->setValue(1); + + setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed)); +} + +// public interface + +int OffsetConstraint::getStride(void) const +{ + return stride->text().toInt(); +} + +int OffsetConstraint::getOffset(void) const +{ + return offset->text().toInt(); +} + +off_t OffsetConstraint::next(off_t off) const +{ + return( ((off-getOffset())/getStride()+1)*getStride()+getOffset() ); +} + +off_t OffsetConstraint::prev(off_t off) const +{ + return( ((off-getOffset())/getStride() - 1)*getStride()+getOffset() ); +} + +// public slots +void OffsetConstraint::setOffset(int x) +{ + offset->setValue( min(x,getStride()-1) ); +} + +void OffsetConstraint::setStride(int x) +{ + // constrain x to (0,MAXINT) + if( x < 1 ) { + stride->setValue(1); + return; + } + + // constrain offset to [0,x) + + // setRange() does not seem to work as the documentation says, so + // we have to manually change the value + if( offset->text().toInt() >= x ) + offset->setValue(x-1); + offset->setRange(0,x-1); + return; +} + diff --git a/offsetConstraint.hpp b/offsetConstraint.hpp new file mode 100644 index 0000000..3e3b919 --- /dev/null +++ b/offsetConstraint.hpp @@ -0,0 +1,48 @@ +/* $Id: offsetConstraint.hpp,v 1.5 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// widget for constraining offset to stride*n+offset + +#include +#include + +class QWidget; +class QSpinBox; + +class OffsetConstraint :public QGroupBox { + Q_OBJECT + +public: + OffsetConstraint( QWidget * parent = 0 ); + + int getStride(void) const; + int getOffset(void) const; + + // applies formula to find next/previous constrained offset + off_t next(off_t off) const; + off_t prev(off_t off) const; + +public slots: + void setStride(int); + void setOffset(int); + +protected: + QSpinBox * stride; + QSpinBox * offset; // [0,stride) + +}; diff --git a/reader.cpp b/reader.cpp new file mode 100644 index 0000000..07e267e --- /dev/null +++ b/reader.cpp @@ -0,0 +1,363 @@ +/* $Id: reader.cpp,v 1.10 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +// for stat: +#include +#include +#include + +// for strerror and errno: +#include +#include + +#include + +#include + +#include "local.h" +#include "reader.hpp" // some systems #define to map fn's to thier 64 bit + // equivalents. make sure the header gets processed the + // same as the .cc file + +Reader::Reader( const string& filename, off_t npages, off_t pageSize ) + : _filename(filename), + _pageSize(pageSize) +{ + _maxPages = _freePages = (npages < 10) ? 10: npages; + _error = ""; + _is_open = false; + _offset = 0; + _size = 0; + _firstPage = -1; + _lastPage = -1; + + if( !filename.empty() ) + open(filename); +} + +Reader::~Reader() +{ + if( _is_open ) + close(); +} + +// +// public methods +// + +bool Reader::open( const string& filename ) +{ + // clear old data + if( is_open() ) { + this->close(); + if( is_open() ) // only occurs if close somehow fails. + return false; + } else { + _is_open = false; + } + + if( 0 == (_fptr = fopen( filename.c_str() , "r" )) ) { + _error = "Failed to open \"" + filename + "\""; + return false; + } + + // get filesize + struct stat statBuf; + if( -1 == stat(filename.c_str(), &statBuf) ) { + _error = "Could not stat \"" + filename + "\""; + return false; + } + + off_t filesize = statBuf.st_size; + _size = filesize; + // calculate necessary number of pages + off_t npages = filesize/_pageSize +1; + // initialize page pointers + _data.resize(npages); + fill( _data.begin(), _data.begin()+npages, (uchar*)0 ); + + _filename = filename; + _is_open = true; + _firstPage = _lastPage = 0; + return loadPage(0); +} + +bool Reader::close() +{ + if( !is_open() ) { + _error = "attempted to close non-open reader."; + return false; + } + + _filename = ""; + _error = ""; + if( EOF == fclose(_fptr) ) { + _error = strerror(errno); + return false; + } + // free data pages + vector::iterator itr; + for( itr = _data.begin(); itr != _data.end(); ++itr ) + delete [] (*itr); + _is_open = false; + _firstPage = _lastPage = -1; + _freePages = _maxPages; + return true; +} + +bool Reader::is_open() const +{ + return _is_open; +} + +size_t Reader::read(vector& v, size_t numBytes) +{ + int lastPageIdx = 0; + size_t bytesRead; + + // if we don't have enough bytes left... then set page_end to lastPageIdx + // to the last page and set bytesRead to the remaining number of bytes + if( _offset+numBytes >= size() ) { + _eof = true; + lastPageIdx = _data.size()-1; + bytesRead = size()-tell(); + // we use numBytes as a counter for the number of operations to make + // so limit numBytes to the actual the max number of bytes left + numBytes = bytesRead; + } else { + lastPageIdx = (_offset+numBytes)/_pageSize; + bytesRead = numBytes; + } + + if( !numBytes ) + return numBytes; + + // only do one realloc if necessary + v.erase(v.begin(),v.end()); + v.reserve(v.size()+numBytes); + + // int byte_end; // == lastByteIndex+1 + // copy the data a page at a time + for(int page = _offset/_pageSize; page <= lastPageIdx; page++) { + // ensure the page is loaded. + try { + loadPage( page ); + } + catch (bad_alloc){ + // out of memory + _error = "Out of memory."; + // return the number of bytes read + return (_offset/_pageSize - page)*_pageSize; + } + + int start = _offset%_pageSize; + int stop = (page == lastPageIdx)? start+numBytes: _pageSize; + for(int i = start; i < stop; i++) { + v.push_back(_data[page][i]); + } + numBytes -= stop-start; + _offset += stop-start; + } + return bytesRead; +} +bool Reader::eof() +{ + return (is_open())? _eof : 0; +} +off_t Reader::seek(off_t offset) +{ + if( !is_open() ) + return -1; + + // seek resets eof... even if the seek is past eof + _eof = false; + + _offset = max( min( offset, size()-1 ), (off_t)0); + if( fseek(_fptr, _offset, SEEK_SET) ) { + _error = "Seek to offset:"; + _error += _offset; + _error += " failed."; + return -1; + } + assert(_offset == ftell(_fptr)); + return _offset; +} + +off_t Reader::tell() const +{ + if(!is_open()) + return -1; + return _offset; +} + +off_t Reader::size() const +{ + return _size; +} + +const char* Reader::lastError() const +{ + return _error.c_str(); +} +const char* Reader::filename() const +{ + return _filename.c_str(); +} + +// +// Protected member fn's +// + +bool Reader::loadPage(off_t pageIdx) +{ + if( !is_open() ) + return false; + if(_data[pageIdx] != 0) + return true; + + if( !nFreePages() ) { + // free the page which is the furthest away from the page we are loading + + // this could be trouble if off_t is unsigned! + if( abs(_firstPage - pageIdx) > abs(_lastPage - pageIdx) ) + while(!freePage(_firstPage++)); + else + while(!freePage(_lastPage--)); + } + _data[pageIdx] = new uchar [_pageSize]; + // page added, subtract from num available + --nFreePages(); + + fseek(_fptr,pageIdx*_pageSize, SEEK_SET); + + off_t retval = fread(_data[pageIdx], sizeof(uchar), _pageSize, _fptr); + + if( retval ) { + if( pageIdx < _firstPage ) + _firstPage = pageIdx; + + if( pageIdx > _lastPage ) + _lastPage = pageIdx; + } + return retval; +} + +bool Reader::freePage(off_t pageIdx) +{ + // check range + if( pageIdx < 0 || (unsigned)pageIdx >= _data.size() || !_data[pageIdx] ) + return false; + + delete [] _data[pageIdx]; + _data[pageIdx] = 0; + ++nFreePages(); // page freed + return true; +} + +uchar Reader::operator[] (off_t offset) +{ + if( !is_open() ) + throw logic_error("Reader::operator[] - attempt to access non-open file."); + if( offset < 0 ) + throw out_of_range("Reader::operator[] - " + "attempt to access negative offset."); + if( offset >= size() ) + throw out_of_range("Reader::operator[] - " + "attempt to access past end of file"); + + off_t page_idx = offset/_pageSize; + assert( loadPage( page_idx ) ); + return _data[page_idx][ offset%_pageSize ]; +} + +off_t +Reader::nFreePages() const +{ + return _freePages; +} + +off_t& +Reader::nFreePages() +{ + return _freePages; +} + +bool +Reader::dataIsAtOffset( const vector& data, off_t pos ) +{ + size_t i = 0; + while( i < data.size() ) { + if( data[i] != (*this)[pos+i] ) { + return false; + } + ++i; + } + return true; +} + + +off_t +Reader::findIndex( off_t start, const vector& data, off_t stop ) +{ + off_t pos = start; + while( pos <= stop ) { + if( data[0] == (*this)[pos] ) { + if( dataIsAtOffset( data, pos ) ) { + return pos; + } + } + ++pos; + } + return size(); +} + +off_t +Reader::rFindIndex( off_t start, const vector& data, off_t stop ) +{ + off_t pos = start; + while( pos >= stop ) { + if( data[0] == (*this)[pos] ) { + if( dataIsAtOffset( data, pos ) ) { + return pos; + } + } + --pos; + } + return size(); +} + +// +// ReadBuffer friends and aquaintances (only for debugging) +// + +#if 0 +ostream& operator<< (ostream&out, const ReadBuffer& buff) +{ + ios_base::fmtflags old_flags = out.flags(); + out.flags(old_flags | ios::hex | ios::showbase); + for(size_t i = 0; i < buff.size(); i++) + out << buff[i]; + // restore old flags + out.flags(old_flags); + return out; +} +#endif diff --git a/reader.hpp b/reader.hpp new file mode 100644 index 0000000..688dc63 --- /dev/null +++ b/reader.hpp @@ -0,0 +1,95 @@ +/* $Id: reader.hpp,v 1.6 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _READER_H_ +#define _READER_H_ + +// +// Interface for Reader object. +// +// Reader object supports: +// open(const QString& filename ); -> open a file for reading +// bool is_open(); -> see if file is open +// close(); -> free resources for open file. +// int read( vector& v, int maxSize ) -> fill v with maxSize data from +// open file. +// off_t seek( off_t offset ); -> move file pointer to offset offset. +// + +#include +#include + +#include +#include +#include + +#include "local.h" + +typedef vector ReadBuffer; + +#ifdef __GNUC__ +ostream& operator<< (ostream&out,const ReadBuffer& buff); +#endif + +class Reader { + public: + // default is only 50*4k = 200k memory image. + Reader(const string& filename = "",off_t npages = 50, off_t pageSize = 4096); + ~Reader(); + + bool open(const string& filename); + bool close(); + bool eof(); + bool is_open() const; + + size_t read( ReadBuffer& v, size_t numBytes ); + off_t seek( off_t offset ); + off_t tell() const; // returns the current offset or -1 if !open + off_t size() const; + const char* lastError() const; + const char* filename() const; + + uchar operator[] (off_t offset); // cannot be const because it does change + // data by loading/swapping pages + off_t findIndex( off_t start, const vector& data, + off_t stop ); + off_t rFindIndex( off_t start, const vector& data, + off_t stop ); + protected: + bool dataIsAtOffset( const vector& data, off_t pos ); + bool loadPage(off_t pageIdx); + bool freePage(off_t pageIdx); + off_t nFreePages() const; + off_t& nFreePages(); + + protected: + string _error; + FILE* _fptr; + bool _is_open; + bool _eof; + string _filename; + off_t _offset; // current offset + off_t _size; // file size from fstat + off_t _pageSize; // number of bytes in a page + off_t _firstPage; // first currently loaded page + off_t _lastPage; // last currently loaded page + off_t _maxPages; // maximum number of pages which could be currently loaded + off_t _freePages; // number of free pages + vector< uchar *> _data; +}; + +#endif diff --git a/save.cpp b/save.cpp new file mode 100644 index 0000000..928e98d --- /dev/null +++ b/save.cpp @@ -0,0 +1,67 @@ +/* $Id: save.cpp,v 1.4 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include + +#include "translate.hpp" +#include "local.h" +#include "delta.hpp" +#include "reader.hpp" + +extern int errno; + +bool writeDeltas( const string& filename , DeltaMap& dmap ) +{ + FILE* fptr = fopen( filename.c_str(), "r+" ); + if( NULL == fptr ) { + QString message = "Error opening \""; + message += filename.c_str(); + message += "\": "; + message += strerror(errno); + QMessageBox::critical( NULL, PROGRAM_STRING, + message, + "Ok"); + return false; + } + + const Delta * deltaPtr; + for(off_t offset = dmap.lower_bound(0); + offset != -1; + offset = dmap.lower_bound(offset+1)) { + if( fseek(fptr,offset,SEEK_SET) ) { + fclose(fptr); + QString message = "Error seeking to offset "; + message += offset; + message += ": "; + message += strerror(errno); + QMessageBox::critical( NULL, PROGRAM_STRING, + message, + "Ok"); + return false; + } + deltaPtr = dmap.search(offset); + fwrite( &(deltaPtr->newData()[0]), sizeof(uchar) , + deltaPtr->newData().size(), fptr); + } + fclose(fptr); + return true; +} diff --git a/save.hpp b/save.hpp new file mode 100644 index 0000000..7af709d --- /dev/null +++ b/save.hpp @@ -0,0 +1,25 @@ + +/* $Id: save.hpp,v 1.3 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _HEXEDIT_SAVE_ +#define _HEXEDIT_SAVE_ + +bool writeDeltas( const string& filename , DeltaMap& dmap ); + +#endif diff --git a/translate.cpp b/translate.cpp new file mode 100755 index 0000000..36a2f63 --- /dev/null +++ b/translate.cpp @@ -0,0 +1,189 @@ +/* $Id: translate.cpp,v 1.4 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "translate.hpp" + +// translation maps from byte->hex, hex->byte, and byte->char +// mappings are static arrays for speed +#include "mappings.h" + +void Translate::CharToByte(vector& dst, const vector& src) +{ + // just copy the base into the byte vector + dst = vector(src.begin(),src.end()); +} +void Translate::CharToByte(vector& dst, const QString& src) +{ + // just copy the base into the byte vector + dst.resize( src.length() ); + for(int i =0 ; i < src.length(); ++i) + dst[i] = src[i].toAscii(); +} + + +void Translate::ByteToChar(vector& dst, const vector& src) +{ + dst.erase(dst.begin(),dst.end()); + dst.reserve(src.size()); + for(unsigned int i = 0; i < src.size(); i++) { + dst.push_back(TranslationTables::byteToCharMap[src[i]]); + } +} + +void Translate::ByteToChar(QString& dst, const vector& src) +{ + dst = ""; + for(unsigned int i = 0; i < src.size(); i++) { + dst += TranslationTables::byteToCharMap[src[i]]; + } +} + +void Translate::HexToByte(vector& dst, const vector& src) +{ + dst.erase(dst.begin(),dst.end()); + int start; + + if( src.size() % 2 ) { + // if odd + dst.reserve(src.size()/2+1); + dst.push_back(TranslationTables::hexToByteMap[src[0]]); + start = 1; + } else { + start = 0; + dst.reserve(src.size()/2); + } + for(unsigned int i = start; i < src.size(); i+=2) { + dst.push_back(TranslationTables::hexToByteMap[src[i]]*16 + + TranslationTables::hexToByteMap[src[i+1]]); + } +} + +void Translate::HexToByte(vector& dst, const QString& src) +{ + dst.erase(dst.begin(),dst.end()); + dst.reserve(src.length()/2); + int i = 0; + if( src.length() % 2 ) { + i = 1; + } + for(; i < src.length(); i+=2) { + uchar hi = src[i].toAscii(); + uchar lo = src[i+1].toAscii(); + dst.push_back( TranslationTables::hexToByteMap[hi]*16 + + TranslationTables::hexToByteMap[lo] ); + } +} + +void Translate::OctalToByte(vector &dst, const vector& src) +{ + dst.erase(dst.begin(),dst.end()); + dst.reserve(src.size()/3); + for(unsigned int i = 0; i+2 < src.size(); i+=3) { + dst.push_back(TranslationTables::hexToByteMap[src[i]]*64+ + TranslationTables::hexToByteMap[src[i+1]]*8+ + TranslationTables::hexToByteMap[src[i+2]]); + } +} + +void Translate::BinaryToByte(vector &dst, const vector& src) +{ + dst.erase(dst.begin(),dst.end()); + dst.reserve(src.size()/8); + // FIXME: would be faster if we compared each src[i] with '1' before mult + for(unsigned int i = 0; i < src.size(); i+=8) { + dst.push_back(TranslationTables::hexToByteMap[src[i]]*128+ + TranslationTables::hexToByteMap[src[i+1]]*64+ + TranslationTables::hexToByteMap[src[i+2]]*32+ + TranslationTables::hexToByteMap[src[i+3]]*16+ + TranslationTables::hexToByteMap[src[i+4]]*8+ + TranslationTables::hexToByteMap[src[i+5]]*4+ + TranslationTables::hexToByteMap[src[i+6]]*2+ + TranslationTables::hexToByteMap[src[i+7]]); + } +} + +const char * Translate::ByteToHex(uchar b) +{ + return TranslationTables::byteToHexMap[b]; +} + +void Translate::ByteToHex(vector& dst, const vector& src) +{ + const char * str; + dst.erase(dst.begin(),dst.end()); + dst.reserve(src.size()*2); + for(unsigned int i = 0; i < src.size(); i++) { + str = TranslationTables::byteToHexMap[src[i]]; + dst.push_back(*str); + dst.push_back(*(str+1)); + } +} + +void Translate::ByteToHex(QString& dst, const vector&src) +{ + dst = ""; + for(unsigned int i = 0; i < src.size(); i++) { + dst += TranslationTables::byteToHexMap[src[i]]; + } +} + +void Translate::ByteToOctal(vector&dst, const vector&src) +{ + dst.erase(dst.begin(),dst.end()); + dst.reserve(src.size()*3); + + vector::const_iterator itr; + const char * str; + for(itr = src.begin(); itr != src.end(); ++itr) { + str = TranslationTables::byteToOctalMap[*itr]; + dst.insert( dst.end(), str, str+3 ); + } +} + +void Translate::ByteToOctal(QString&dst, const vector&src) +{ + dst = ""; + vector::const_iterator itr; + for(itr = src.begin(); itr != src.end(); ++itr) { + dst += TranslationTables::byteToOctalMap[*itr]; + } +} + +void Translate::ByteToBinary(vector&dst, const vector&src) +{ + dst.erase(dst.begin(),dst.end()); + dst.reserve(src.size()*8); + + vector::const_iterator itr; + const char * str; + for(itr = src.begin(); itr != src.end(); ++itr) { + str = TranslationTables::byteToBinaryMap[*itr]; + dst.insert(dst.end(), str, str+8); + } +} + +void Translate::ByteToBinary(QString&dst, const vector&src) +{ + dst = ""; + + vector::const_iterator itr; + for(itr = src.begin(); itr != src.end(); ++itr) { + dst += TranslationTables::byteToBinaryMap[*itr]; + } +} + diff --git a/translate.hpp b/translate.hpp new file mode 100755 index 0000000..95582eb --- /dev/null +++ b/translate.hpp @@ -0,0 +1,47 @@ +/* $Id: translate.hpp,v 1.4 2006/11/05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn + * + * 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 version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include + +#include "local.h" + +typedef unsigned char uchar; + +namespace Translate { + const char* ByteToHex(uchar b); + void ByteToHex(vector& dst , const vector& src); + void ByteToChar(vector& dst, const vector& src); + void ByteToOctal(vector& dst,const vector& src); + void ByteToBinary(vector& dst,const vector& src); + + void ByteToHex(QString& dst , const vector& src); + void ByteToChar(QString& dst, const vector& src); + void ByteToOctal(QString& dst,const vector& src); + void ByteToBinary(QString& dst,const vector& src); + + void BinaryToByte(vector& dst,const vector& src); + + void OctalToByte(vector& dst,const vector& src); + + void CharToByte(vector& dst, const vector& src); + void CharToByte(vector& dst, const QString& src); + + void HexToByte(vector& dst , const vector& src); + void HexToByte(vector& dst , const QString& src); + +}; -- cgit v1.2.3-54-g00ecf