I am trying to reconstruct an image from a file which is in Intel hex 386 format. After parsing the file all the data I am copying to a QByteArray and same array is used to create a QImage Object. But whatever image is which I got after reconstructing is not perfect. I am getting blue color instead of black, edges are not clear etc. The text file which I am parsing is a ram memory dump from STM32F4 controller (arm).The image is stored in RGB565 format.
Code to create the image:
{
QString strFilename;
Hex386Parser oFileParser;
strFilename = QFileDialog::getOpenFileName(this,"Select a file", QDir::homePath());
oFileParser.parseFile(strFilename, oByteArray);
QImage image(320, 240, QImage::Format_RGB16);
for (int y = 0; y < image.height(); y++)
{
memcpy(image.scanLine(y), oByteArray.constData() + y * image.bytesPerLine(),
image.bytesPerLine());
}
qDebug() <<"Size of the byte array is " <<oByteArray.size();
QLabel *label = new QLabel();
label->setPixmap(QPixmap::fromImage(image));
label->show();
}
Code to used to parse the file:
#define QT_NO_CAST_TO_ASCII
void Hex386Parser::parseFile(QString strFilename,QByteArray& ref_ByteArray)
{
QFile oFile(strFilename);
std::stringstream sstr;
QString strLength;
int unLength = 0, unAddress = 0,unDescriptor =0xFFFF,nIndex =0,nlineno=0;
if (oFile.open((QIODevice::ReadOnly | QIODevice::Text)))
{
QTextStream in(&oFile);
while (!in.atEnd())
{
QString line = in.readLine();
nIndex = 0;
nlineno++;
//unsigned char *pCharFrame = (unsigned char *)line.toStdString().c_str();
if (':' != line.at(nIndex))
{
// file corrupted
return;
}
nIndex++;
{
strLength = line.mid(nIndex, 2);
sstr << strLength.toStdString();
sstr << std::hex;
sstr >> unLength; // get length of the record
strLength.clear();
sstr.clear();
}
nIndex += 2;
unAddress = line.mid(nIndex,4).toInt(); // get address bytes
nIndex +=4;
unDescriptor = line.mid(nIndex, 2).toInt(); // get data descriptor
nIndex += 2;
switch(unDescriptor)
{
case data_record:
ref_ByteArray.append((line.mid(nIndex, unLength )));
// add data to bytearray
break;
case end_of_file_record:
break;
case extended_segment_address_record:
break;
case extended_linear_address_record:
break;
case start_linear_address_record:
break;
}
}
oFile.close();
}
}
What am I doing wrong??
The line contains hex string data representations where each byte is coded as two characters.
You want binary bytes. So, 2 * unLength symbols should be read from line. Then, that data string should converted to binary, for example:
{
case data_record:
QByteArray hex = line.mid(nIndex, 2 * unLength ).toLatin1();
QByteArray binary = QByteArray::fromHex(hex);
ref_ByteArray.append(binary);
...
}
Related
I am working on an application that works as a "man in the middle" to analyze a protocol (ISO 8583) sent over TCP/IP.
The main idea is to get the raw binary data and convert it to a string for parsing and decoding the protocol.
For this, I am using the TIdMappedPortTCP component.
I am testing with Hercules.
I am working with:
Windows 11 Home
Embarcadero® C++Builder 10.4 Version 27.0.40680.4203
Delphi and C++ Builder 10.4 Update 2
Indy 10.6.2.0
More context can be found in these questions:
Where can I find a fully working example of a TCP Client and Server for Indy in C++Builder?
Parsing bytes as BCD with Indy C++ Builder
The problem is that I have to send the message twice to trigger the OnExecute event. I think this might be length related but I haven't found the issue. Other than that the program does what is expected from it.
If I use this data in Hercules:
00 04 60 02
equivalent to:
"\x00\x04\x60\x02"
My program processes everything correctly:
Here is the code:
void __fastcall TForm1::MITMProxyExecute(TIdContext *AContext)
{
static int index;
TIdBytes ucBuffer;
UnicodeString usTemp1;
UnicodeString usTemp2;
int calculated_length;
// getting the length in Hexa
calculated_length = ReadMessageLength(AContext);
// reads data
AContext->Connection->IOHandler->ReadBytes(ucBuffer, calculated_length);
// displays string with calculated length and size of the data
usTemp2 = UnicodeString("calculated length = ");
usTemp2 += IntToStr(calculated_length);
usTemp2 += " ucBuffer.Length = ";
usTemp2 += IntToStr(ucBuffer.Length);
Display->Lines->Add(usTemp2);
// converts the binary data into a a Hex String for visualization
usTemp1 = BytesToHexString(ucBuffer);
// adds an index to distinguish from previous entries.
usTemp2 = IntToStr(index);
usTemp2 += UnicodeString(": ");
usTemp2 += usTemp1;
Display->Lines->Add(usTemp2);
index++;
}
Here is the code for the functions called there. By the way, is there a better way to convert the bytes to a hex string?
// Convert an array of bytes to a hexadecimal string
UnicodeString BytesToHexString(const TBytes& bytes)
{
// Create an empty UnicodeString to store the hexadecimal representation of the bytes
UnicodeString hexString;
// Iterate through each byte in the array
for (int i = 0; i < bytes.Length; i++)
{
// Convert the byte to a hexadecimal string and append it to the result string
hexString += IntToHex(bytes[i], 2);
}
// Return the hexadecimal string
return hexString;
}
// Read the first two bytes of an incoming message and interpret them as the length of the message
int ReadMessageLength(TIdContext *AContext)
{
int calculated_length;
// Use the 'ReadSmallInt' method to read the length of the message from the first two bytes
calculated_length = AContext->Connection->IOHandler->ReadSmallInt();
// converting from hex binary to hex string
UnicodeString bcdLength = UnicodeString().sprintf(L"%04x", calculated_length);
// converting from hex string to int
calculated_length = bcdLength.ToInt();
// decrease length
calculated_length -= 2;
return calculated_length;
}
UPDATE
I have created a class to update the TEditRich control. But the problem persist, I need to send the message twice to be processed and the application freezes when trying to close it. This is my class:
class TAddTextToDisplay : public TIdSync {
private:
UnicodeString textToAdd;
public:
__fastcall TAddTextToDisplay(UnicodeString str) {
// Store the input parameters in member variables.
textToAdd = str;
}
virtual void __fastcall DoSynchronize() {
if (textToAdd != NULL) {
// Use the input parameters here...
Form1->Display->Lines->Add(textToAdd);
}
}
void __fastcall setTextToAdd(UnicodeString str) {
textToAdd = str;
}
};
And this is how my new OnExecute event looks:
void __fastcall TForm1::MITMProxyExecute(TIdContext *AContext) {
static int index;
TIdBytes ucBuffer;
UnicodeString usTemp1;
UnicodeString usTemp2;
int calculated_length;
int bytes_remaining;
// getting the length in Hexa
calculated_length = ReadMessageLength(AContext);
if (!AContext->Connection->IOHandler->InputBufferIsEmpty()) {
// reads data
AContext->Connection->IOHandler->ReadBytes(ucBuffer, calculated_length);
// displays string with calculated length and size of the data
usTemp2 = UnicodeString("calculated length = ");
usTemp2 += IntToStr(calculated_length);
usTemp2 += " ucBuffer.Length = ";
usTemp2 += IntToStr(ucBuffer.Length);
TAddTextToDisplay *AddTextToDisplay = new TAddTextToDisplay(usTemp2);
AddTextToDisplay->Synchronize();
// converts the binary data into a a Hex String for visualization
usTemp1 = BytesToHexString(ucBuffer);
// adds an index to distinguish from previous entries.
usTemp2 = IntToStr(index);
usTemp2 += UnicodeString(": ");
usTemp2 += usTemp1;
AddTextToDisplay->setTextToAdd(usTemp2);
AddTextToDisplay->Synchronize();
delete AddTextToDisplay;
index++;
}
}
You really should not be reading from the IOHandler directly at all. You are getting your communication out of sync. TIdMappedPortTCP internally reads from the client before firing the OnExecute event, and reads from the target server before firing the OnOutboundData event. In both cases, the bytes received are made available in the TIdMappedPortContext::NetData property, which you are not processing at all.
You need to do all of your parsing using just the NetData only, iterating through its bytes looking for complete messages, and saving incomplete messages for future events to finish.
Try something more like this instead:
#include <IdGlobal.hpp>
#include <IdBuffer.hpp>
bool ReadMessageData(TIdBuffer *Buffer, int &Offset, TIdBytes &Data)
{
// has enough bytes?
if ((Offset + 2) > Buffer->Size)
return false;
// read the length of the message from the first two bytes
UInt16 binLength = Buffer->ExtractToUInt16(Offset);
// converting from hex binary to hex string
String bcdLength = String().sprintf(_D("%04hx"), binLength);
// converting from hex string to int
int calculated_length = bcdLength.ToInt() - 2;
// has enough bytes?
if ((Offset + 2 + calculated_length) > Buffer->Size)
return false;
// reads data
Data.Length = calculated_length;
Buffer->ExtractToBytes(Data, calculated_length, false, Offset + 2);
Offset += (2 + calculated_length);
return true;
}
void __fastcall TForm1::MITMProxyConnect(TIdContext *AContext)
{
AContext->Data = new TIdBuffer;
}
void __fastcall TForm1::MITMProxyDisconnect(TIdContext *AContext)
{
delete static_cast<TIdBuffer*>(AContext->Data);
AContext->Data = NULL;
}
void __fastcall TForm1::MITMProxyExecute(TIdContext *AContext)
{
static int index = 0;
TIdBuffer *Buffer = static_cast<TIdBuffer*>(AContext->Data);
Buffer->Write(static_cast<TIdMappedPortContext*>(AContext)->NetData);
Buffer->CompactHead();
TAddTextToDisplay *AddTextToDisplay = NULL;
TIdBytes ucBuffer;
int offset = 0;
while (ReadMessageData(Buffer, offset, ucBuffer))
{
String sTemp = String().sprintf(_D("%d: ucBuffer.Length = %d ucBuffer = %s"), index, ucBuffer.Length, ToHex(ucBuffer).c_str());
if (AddTextToDisplay)
AddTextToDisplay->setTextToAdd(sTemp);
else
AddTextToDisplay = new TAddTextToDisplay(sTemp);
AddTextToDisplay->Synchronize();
++index;
}
delete AddTextToDisplay;
if (offset > 0)
Buffer->Remove(offset);
}
Otherwise, if you want to do your own socket I/O, then you will have to use TIdTCPServer and TIdTCPClient directly instead of using TIdMappedPortTCP.
I have a qTextEdit that I grab the text from (QString) and convert to a char* with this code:
QString msgQText = ui->textMsg->toPlainText();
size_t textSize = (size_t)msgQText.size();
if (textSize > 139) {
textSize = 139;
}
unsigned char * msgText = (unsigned char *)malloc(textSize);
memcpy(msgText, msgQText.toLocal8Bit().data(), textSize);
msgText[textSize] = '\0';
if (textSize > 0) {
Msg * newTextMsg = new Msg;
newTextMsg->type = 1; // text message type
newTextMsg->bitrate = 0;
newTextMsg->samplerate = 0;
newTextMsg->bufSize = (int)textSize;
newTextMsg->len = 0;
newTextMsg->buf = (char *)malloc(textSize);
memcpy((char *)newTextMsg->buf, (char *)msgText, textSize);
lPushToEnd(sendMsgList, newTextMsg, sizeof(Msg));
ui->sendRecList->addItem((char *)newTextMsg->buf);
ui->textMsg->clear();
}
I put the text into a qListBox, but it shows up like
However, the character array, if I print it out, does not have the extra characters.
I have tried checking the "compile using UTF-8" option, but it doesn't make a difference.
Also, I send the text using RS232, and the receiver side also displays the extra characters.
The receiver code is here:
m_serial->waitForReadyRead(200);
const QByteArray data = m_serial->readAll();
if (data.size() > 0) {
qDebug() << "New serial data: " << data;
QString str = QString(data);
if (str.contains("0x6F8C32E90A")) {
qDebug() << "TEST SUCCESSFUL!";
}
return data.data();
} else {
return NULL;
}
There is a difference between the size of a QString and the size of the QByteArray returned by toLocal8Bit(). A QString contains unicode text stored as UTF-16, while a QByteArray is "just" a char[].
A QByteArray is null-terminated, so you do not need to add it manually.
As #GM pointed out: msgText[textSize] = '\0'; is undefined behavior. You are writing to the textSize + 1 position of the msgText array.
This position may be owned by something else and may be overwritten, so you end up with a non null terminated string.
This should work:
QByteArray bytes = msgQText.toLocal8Bit();
size_t textSize = (size_t)bytes.size() + 1; // Add 1 for the final '\0'
unsigned char * msgText = (unsigned char *) malloc(textSize);
memcpy(msgText, bytes.constData(), textSize);
Additional tips:
Prefer using const functions on Qt types that are copy-on-write, e.g. use QBytearray::constData() instead of QByteArray::data(). The non-const functions can cause a deep-copy of the object.
Do not use malloc() and other C-style functions if possible. Here you could do:
unsigned char * msgText = new unsigned char[textSize]; and later delete[] msgText;.
Prefer using C++ casts (static_cast, reinterpret_cast, etc.) instead of C-style casts.
You are making 2 copies of the text (2 calls to memcpy), given your code only 1 seem to be enough.
I am storing some data in QDataStream and immediately taking the data, but the count is showing zero while retriving. code looks fine but unexpected behaviour
//Overloading
QDataStream& operator<< (QDataStream& writeTO, const CascadeJobInfo& data)
{
writeTO << data.m_infoJobType << data.m_connectionName << data.m_submitJobId << data.m_submitJobStat;
return writeTO;
}
QDataStream& operator>> (QDataStream& readIn, CascadeJobInfo& data)
{
readIn >> data.m_infoJobType >> data.m_connectionName >> data.m_submitJobId >> data.m_submitJobStat;
return readIn;
}
void Fun()
{
// Code Starts here
projectFileName = /*Path to folder*/
QFile file(projectFileName);
file.open(QFile::ReadWrite);
file.close();
QDataStream dStream(&file);
int jobLstCount = /*Get the Count, assume 4*/
dStream << jobLstCount;
for(int i = 0; i < jobLstCount; i++)
{
JobInfo.m_infoJobType = jobFlowItem->getJobType();
JobInfo.m_connectionName = submitItem->connectionName();
JobInfo.m_submitJobId = submitItem->jobID();
JobInfo.m_submitJobStat = submitItem->jobState();
// All valid data stored here
}
file.close();
QDataStream dStreamOut(&file);
dStreamOut >> jobLstCount; /*Count returns zero here insted of 4*/
CascadeJobInfo jobInfo;
// Why jobLstCount is getting zero here
for(int i = 0 ; i < jobLstCount ; i++)
{
dStreamOut >> jobInfo;
}
}
file.open(QFile::ReadWrite);
file.close(); <--- HERE
QDataStream dStream(&file);
You are closing the file as soon as you open it, so basically you are working with an invalid file descriptor which won't work. Put file.close() at the end of the code when you are done.
I am writing a data logger and would like to keep the files limited to a specific number of entries. I am trying to write this bit of code in the setup, so that when the Arduino powers on, it will write to a new file just to keep things simple. However, when I try to open the file I can't, although I am not sure why. Can anyone offer any explanation?
char *fileName; //global name
File logFile; //global file
//everything else is in setup()
char * topPart = "/Data/Data"; //first part of every file
char * lowerPart = ".txt"; // jus the extention
char * itter; //used to hold the char of i later
fileName = "/Data/Data.txt"; //start with the first file possible.
for(int i=0; i<=100;i++) {
if(!SD.exists(fileName)) {
Serial.print("opening file: ");
Serial.print(fileName);
logFile = SD.open(fileName, FILE_WRITE);
if(logFile) {
logFile.println("I made it");
Serial.println("in the file");
}
if(!logFile) {
Serial.println("somthing bad");
}
break;
} else {
itter = (char *)(i+48);
strcpy(fileName,topPart);
strcpy(fileName,itter);
strcpy(fileName,lowerPart);
Serial.println(i);
}
}
Lots of problems.
the construction of itter is wrong.
strcpy doesn't append just cpy.
Here is a code example to build your filename. This a basic C program. Remove the #include and main for Arduino, this allows to test on your computer whether the program is ok.
#include <string.h>
#define TOPPART "/Data/Data"
#define LOWERPART ".txt"
int main(void) {
char buf[64];
snprintf(buf, sizeof(buf), "%s%s", TOPPART, LOWERPART);
for (int i = 0; i < 100; i++) {
/* here your stuff to check if the filename froml buf exists*/
snprintf(buf, sizeof(buf), "%s%d%s", TOPPART, i, LOWERPART);
}
return 0;
}
Pointer related question. I'm going through some example code that currently reads in data from a file called dataFile into a buffer. The reading is done inside a loop as follows:
unsigned char* buffer = (unsigned char*)malloc(1024*768*);
fread(buffer,1,1024*768,dataFile);
redPointer = buffer;
bluePointer = buffer+1024;
greenPointer = buffer+768;
Now, I want to try and write the entire contents of the array buffer to a file, so that I can save just those discrete images (and not have a large file). However, I am not entirely sure how to go about doing this.
I was trying to cout statements, however I get a print-out of garbage characters on the console and also a beep from the PC. So then I end my program.
Is there an alternative method other than this:
for (int i=0; i < (1024*768); i++) {
fprintf(myFile, "%6.4f , ", buffer[i]);
}
By declaring your buffer as a char*, any pointer arithmatic or array indexes will use sizeof(char) to calculate the offset. A char is 1 byte (8 bits).
I'm not sure what you are trying to do with the data in your buffer. Here are some ideas:
Print the value of each byte in decimal, encoded as ASCII text:
for (int i=0; i < (1024*768); i++) {
fprintf(myFile, "%d , ", buffer[i]);
}
Print the value of each byte in hexadecimal, encoded in ASCII text:
for (int i=0; i < (1024*768); i++) {
fprintf(myFile, "%x , ", buffer[i]);
}
Print the value of each floating point number, in decimal, encoded in ASCII text (I think my calculation of the array index is correct to process adjacent non-overlapping memory locations for each float):
for (int i=0; i < (1024*768); i += sizeof(float)) {
fprintf(myFile, "%6.4f , ", buffer[i]);
}
Split the buffer into three files, each one from a non-overlapping section of the buffer:
fwrite(redPointer, sizeof(char), 768, file1);
fwrite(greenPointer, sizeof(char), 1024-768, file2);
fwrite(bluePointer, sizeof(char), (1024*768)-1024, file3);
Reference for fwrite. Note that for the count parameter I simply hard-coded the offsets that you had hard-coded in your question. One could also subtract certain of the pointers to calculate the number of bytes in each region. Note also that the contents of these three files will only be sensible if those are sensibly independent sections of the original data.
Maybe this gives you some ideas.
Updated: so I created a complete program to compile and test the formatting behavior. This only prints the first 20 items from the buffer. It compiles (with gcc -std=c99) and runs. I created the file /tmp/data using ghex and simply filled in some random data.
#include <stdlib.h>
#include <stdio.h>
int main()
{
FILE* dataFile = fopen("/tmp/data", "rb");
if (dataFile == NULL)
{
printf("fopen() failed");
return -2;
}
unsigned char* buffer = (unsigned char*)malloc(1024*768);
if (buffer == NULL)
{
printf("malloc failed");
return -1;
}
const int bytesRead = fread(buffer,1,1024*768,dataFile);
printf("fread() read %d bytes\n", bytesRead);
// release file handle
fclose(dataFile); dataFile = NULL;
printf("\nDecimal:\n");
for (int i=0; i < (1024*768); i++) {
printf("%hd , ", buffer[i]);
if (i > 20) { break; }
}
printf("\n");
printf("\nHexadecimal:\n");
for (int i=0; i < (1024*768); i++) {
printf("%#0hx , ", buffer[i]);
if (i > 20) { break; }
}
printf("\n");
printf("\nFloat:\n");
for (int i=0; i < (1024*768); i += sizeof(float)) {
printf("%6.4f , ", (float)buffer[i]);
if (i > 20) { break; }
}
printf("\n");
return 0;
}