Skip to content

Commit b929744

Browse files
committed
Release 1.0
1 parent d16229d commit b929744

17 files changed

+2661
-572
lines changed

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1-
A simple experimental hex editor (Qt5/C++11).
1+
# Fhex - A Fucking HexEditor
22

3-
Known limitations: Actually the UI doesn't handle files bigger than 1MB. The frontend needs to be changed.
3+
This project is born with the aim to develop a lightweight, but useful tool. The reason is that the existing hex editors have some different limitations (e.g. too many dependencies, missing hex coloring features, etc.).
4+
5+
![screenshot](screenshot.png)
6+
7+
It is still under development, but actually it has the basic features to make it acceptable.
8+
9+
This project is based on **qhexedit2**. New features should be added in the future, PRs are welcomed.
10+
11+
### License
12+
GPL-3

bin/fhex

3.82 MB
Binary file not shown.

bin/fhex_x64_1.0

237 KB
Binary file not shown.

fhex.cpp

Lines changed: 0 additions & 516 deletions
This file was deleted.

fhex.pro

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,16 @@ DEFINES += QT_DEPRECATED_WARNINGS
2424

2525

2626
SOURCES += \
27-
main.cpp \
28-
fhex.cpp \
29-
core/hexeditor.cpp
27+
src/chunks.cpp \
28+
src/commands.cpp \
29+
src/main.cpp \
30+
src/fhex.cpp \
31+
src/core/hexeditor.cpp \
32+
src/qhexedit.cpp
3033

3134
HEADERS += \
32-
fhex.h \
33-
core/hexeditor.h
35+
src/chunks.h \
36+
src/commands.h \
37+
src/fhex.h \
38+
src/core/hexeditor.h \
39+
src/qhexedit.h

screenshot.png

98.9 KB
Loading

src/chunks.cpp

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
#include "chunks.h"
2+
#include <limits.h>
3+
4+
#define NORMAL 0
5+
#define HIGHLIGHTED 1
6+
7+
#define BUFFER_SIZE 0x10000
8+
#define CHUNK_SIZE 0x1000
9+
#define READ_CHUNK_MASK Q_INT64_C(0xfffffffffffff000)
10+
11+
// ***************************************** Constructors and file settings
12+
13+
Chunks::Chunks(QObject *parent): QObject(parent)
14+
{
15+
QBuffer *buf = new QBuffer(this);
16+
setIODevice(*buf);
17+
}
18+
19+
Chunks::Chunks(QIODevice &ioDevice, QObject *parent): QObject(parent)
20+
{
21+
setIODevice(ioDevice);
22+
}
23+
24+
bool Chunks::setIODevice(QIODevice &ioDevice)
25+
{
26+
_ioDevice = &ioDevice;
27+
bool ok = _ioDevice->open(QIODevice::ReadOnly);
28+
if (ok) // Try to open IODevice
29+
{
30+
_size = _ioDevice->size();
31+
_ioDevice->close();
32+
}
33+
else // Fallback is an empty buffer
34+
{
35+
QBuffer *buf = new QBuffer(this);
36+
_ioDevice = buf;
37+
_size = 0;
38+
}
39+
_chunks.clear();
40+
_pos = 0;
41+
return ok;
42+
}
43+
44+
45+
// ***************************************** Getting data out of Chunks
46+
47+
QByteArray Chunks::data(qint64 pos, qint64 maxSize, QByteArray *highlighted)
48+
{
49+
qint64 ioDelta = 0;
50+
int chunkIdx = 0;
51+
52+
Chunk chunk;
53+
QByteArray buffer;
54+
55+
// Do some checks and some arrangements
56+
if (highlighted)
57+
highlighted->clear();
58+
59+
if (pos >= _size)
60+
return buffer;
61+
62+
if (maxSize < 0)
63+
maxSize = _size;
64+
else
65+
if ((pos + maxSize) > _size)
66+
maxSize = _size - pos;
67+
68+
_ioDevice->open(QIODevice::ReadOnly);
69+
70+
while (maxSize > 0)
71+
{
72+
chunk.absPos = LLONG_MAX;
73+
bool chunksLoopOngoing = true;
74+
while ((chunkIdx < _chunks.count()) && chunksLoopOngoing)
75+
{
76+
// In this section, we track changes before our required data and
77+
// we take the editdet data, if availible. ioDelta is a difference
78+
// counter to justify the read pointer to the original data, if
79+
// data in between was deleted or inserted.
80+
81+
chunk = _chunks[chunkIdx];
82+
if (chunk.absPos > pos)
83+
chunksLoopOngoing = false;
84+
else
85+
{
86+
chunkIdx += 1;
87+
qint64 count;
88+
qint64 chunkOfs = pos - chunk.absPos;
89+
if (maxSize > ((qint64)chunk.data.size() - chunkOfs))
90+
{
91+
count = (qint64)chunk.data.size() - chunkOfs;
92+
ioDelta += CHUNK_SIZE - chunk.data.size();
93+
}
94+
else
95+
count = maxSize;
96+
if (count > 0)
97+
{
98+
buffer += chunk.data.mid(chunkOfs, (int)count);
99+
maxSize -= count;
100+
pos += count;
101+
if (highlighted)
102+
*highlighted += chunk.dataChanged.mid(chunkOfs, (int)count);
103+
}
104+
}
105+
}
106+
107+
if ((maxSize > 0) && (pos < chunk.absPos))
108+
{
109+
// In this section, we read data from the original source. This only will
110+
// happen, whe no copied data is available
111+
112+
qint64 byteCount;
113+
QByteArray readBuffer;
114+
if ((chunk.absPos - pos) > maxSize)
115+
byteCount = maxSize;
116+
else
117+
byteCount = chunk.absPos - pos;
118+
119+
maxSize -= byteCount;
120+
_ioDevice->seek(pos + ioDelta);
121+
readBuffer = _ioDevice->read(byteCount);
122+
buffer += readBuffer;
123+
if (highlighted)
124+
*highlighted += QByteArray(readBuffer.size(), NORMAL);
125+
pos += readBuffer.size();
126+
}
127+
}
128+
_ioDevice->close();
129+
return buffer;
130+
}
131+
132+
bool Chunks::write(QIODevice &iODevice, qint64 pos, qint64 count)
133+
{
134+
if (count == -1)
135+
count = _size;
136+
bool ok = iODevice.open(QIODevice::WriteOnly);
137+
if (ok)
138+
{
139+
for (qint64 idx=pos; idx < count; idx += BUFFER_SIZE)
140+
{
141+
QByteArray ba = data(idx, BUFFER_SIZE);
142+
iODevice.write(ba);
143+
}
144+
iODevice.close();
145+
}
146+
return ok;
147+
}
148+
149+
150+
// ***************************************** Set and get highlighting infos
151+
152+
void Chunks::setDataChanged(qint64 pos, bool dataChanged)
153+
{
154+
if ((pos < 0) || (pos >= _size))
155+
return;
156+
int chunkIdx = getChunkIndex(pos);
157+
qint64 posInBa = pos - _chunks[chunkIdx].absPos;
158+
_chunks[chunkIdx].dataChanged[(int)posInBa] = char(dataChanged);
159+
}
160+
161+
bool Chunks::dataChanged(qint64 pos)
162+
{
163+
QByteArray highlighted;
164+
data(pos, 1, &highlighted);
165+
return bool(highlighted.at(0));
166+
}
167+
168+
169+
// ***************************************** Search API
170+
171+
qint64 Chunks::indexOf(const QByteArray &ba, qint64 from)
172+
{
173+
qint64 result = -1;
174+
QByteArray buffer;
175+
176+
for (qint64 pos=from; (pos < _size) && (result < 0); pos += BUFFER_SIZE)
177+
{
178+
buffer = data(pos, BUFFER_SIZE + ba.size() - 1);
179+
int findPos = buffer.indexOf(ba);
180+
if (findPos >= 0)
181+
result = pos + (qint64)findPos;
182+
}
183+
return result;
184+
}
185+
186+
qint64 Chunks::lastIndexOf(const QByteArray &ba, qint64 from)
187+
{
188+
qint64 result = -1;
189+
QByteArray buffer;
190+
191+
for (qint64 pos=from; (pos > 0) && (result < 0); pos -= BUFFER_SIZE)
192+
{
193+
qint64 sPos = pos - BUFFER_SIZE - (qint64)ba.size() + 1;
194+
if (sPos < 0)
195+
sPos = 0;
196+
buffer = data(sPos, pos - sPos);
197+
int findPos = buffer.lastIndexOf(ba);
198+
if (findPos >= 0)
199+
result = sPos + (qint64)findPos;
200+
}
201+
return result;
202+
}
203+
204+
205+
// ***************************************** Char manipulations
206+
207+
bool Chunks::insert(qint64 pos, char b)
208+
{
209+
if ((pos < 0) || (pos > _size))
210+
return false;
211+
int chunkIdx;
212+
if (pos == _size)
213+
chunkIdx = getChunkIndex(pos-1);
214+
else
215+
chunkIdx = getChunkIndex(pos);
216+
qint64 posInBa = pos - _chunks[chunkIdx].absPos;
217+
_chunks[chunkIdx].data.insert(posInBa, b);
218+
_chunks[chunkIdx].dataChanged.insert(posInBa, char(1));
219+
for (int idx=chunkIdx+1; idx < _chunks.size(); idx++)
220+
_chunks[idx].absPos += 1;
221+
_size += 1;
222+
_pos = pos;
223+
return true;
224+
}
225+
226+
bool Chunks::overwrite(qint64 pos, char b)
227+
{
228+
if ((pos < 0) || (pos >= _size))
229+
return false;
230+
int chunkIdx = getChunkIndex(pos);
231+
qint64 posInBa = pos - _chunks[chunkIdx].absPos;
232+
_chunks[chunkIdx].data[(int)posInBa] = b;
233+
_chunks[chunkIdx].dataChanged[(int)posInBa] = char(1);
234+
_pos = pos;
235+
return true;
236+
}
237+
238+
bool Chunks::removeAt(qint64 pos)
239+
{
240+
if ((pos < 0) || (pos >= _size))
241+
return false;
242+
int chunkIdx = getChunkIndex(pos);
243+
qint64 posInBa = pos - _chunks[chunkIdx].absPos;
244+
_chunks[chunkIdx].data.remove(posInBa, 1);
245+
_chunks[chunkIdx].dataChanged.remove(posInBa, 1);
246+
for (int idx=chunkIdx+1; idx < _chunks.size(); idx++)
247+
_chunks[idx].absPos -= 1;
248+
_size -= 1;
249+
_pos = pos;
250+
return true;
251+
}
252+
253+
254+
// ***************************************** Utility functions
255+
256+
char Chunks::operator[](qint64 pos)
257+
{
258+
return data(pos, 1)[0];
259+
}
260+
261+
qint64 Chunks::pos()
262+
{
263+
return _pos;
264+
}
265+
266+
qint64 Chunks::size()
267+
{
268+
return _size;
269+
}
270+
271+
int Chunks::getChunkIndex(qint64 absPos)
272+
{
273+
// This routine checks, if there is already a copied chunk available. If os, it
274+
// returns a reference to it. If there is no copied chunk available, original
275+
// data will be copied into a new chunk.
276+
277+
int foundIdx = -1;
278+
int insertIdx = 0;
279+
qint64 ioDelta = 0;
280+
281+
282+
for (int idx=0; idx < _chunks.size(); idx++)
283+
{
284+
Chunk chunk = _chunks[idx];
285+
if ((absPos >= chunk.absPos) && (absPos < (chunk.absPos + chunk.data.size())))
286+
{
287+
foundIdx = idx;
288+
break;
289+
}
290+
if (absPos < chunk.absPos)
291+
{
292+
insertIdx = idx;
293+
break;
294+
}
295+
ioDelta += chunk.data.size() - CHUNK_SIZE;
296+
insertIdx = idx + 1;
297+
}
298+
299+
if (foundIdx == -1)
300+
{
301+
Chunk newChunk;
302+
qint64 readAbsPos = absPos - ioDelta;
303+
qint64 readPos = (readAbsPos & READ_CHUNK_MASK);
304+
_ioDevice->open(QIODevice::ReadOnly);
305+
_ioDevice->seek(readPos);
306+
newChunk.data = _ioDevice->read(CHUNK_SIZE);
307+
_ioDevice->close();
308+
newChunk.absPos = absPos - (readAbsPos - readPos);
309+
newChunk.dataChanged = QByteArray(newChunk.data.size(), char(0));
310+
_chunks.insert(insertIdx, newChunk);
311+
foundIdx = insertIdx;
312+
}
313+
return foundIdx;
314+
}
315+
316+
317+
#ifdef MODUL_TEST
318+
int Chunks::chunkSize()
319+
{
320+
return _chunks.size();
321+
}
322+
323+
#endif

0 commit comments

Comments
 (0)