-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcServer.cpp
More file actions
504 lines (428 loc) · 14.9 KB
/
cServer.cpp
File metadata and controls
504 lines (428 loc) · 14.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
//------------------------------------------------------------------------
// Copyright(c) 2024 Dad Design.
//
// Utility to flash QSPIFlash memory from a PC
// Server management
//-----------------------------------------------------------------------
#include "pch.h"
#include <algorithm>
#include <windows.h>
#include <stdio.h>
#include <fstream>
#include "cServer.h"
// Server management class
// =======================
// Constructor to initialize the COM port handle and set the start and end markers
Dad::cServer::cServer() {
m_hCom = INVALID_HANDLE_VALUE;
memcpy_s(m_Bloc.StartMarker, sizeof(m_Bloc.StartMarker), "BLOC", 4);
memcpy_s(m_Bloc.EndMarker, sizeof(m_Bloc.EndMarker), "END", 3);
}
// Destructor to close the COM port handle if it is open
Dad::cServer::~cServer() {
ResetClass();
}
// Release the port and buffer memory
void Dad::cServer::ResetClass() {
if (m_hCom != INVALID_HANDLE_VALUE) {
CloseHandle(m_hCom);
m_hCom = INVALID_HANDLE_VALUE;
}
if (nullptr != m_pBuff) {
delete[] m_pBuff;
m_pBuff = nullptr;
m_pEndBuff = nullptr;
m_pFirstFreeBuff = nullptr;
m_pFile = nullptr;
}
}
// Configure the COM port with the specified parameters
bool Dad::cServer::Config(DWORD BaudRate, BYTE ByteSize, BYTE Parity, BYTE StopBits) {
// Get the current COM port configuration
if (!GetCommState(m_hCom, &m_Config)) {
return false;
}
// Set the new COM port configuration
m_Config.BaudRate = BaudRate;
m_Config.ByteSize = ByteSize;
m_Config.Parity = Parity;
m_Config.StopBits = StopBits;
// Apply the new configuration
if (!SetCommState(m_hCom, &m_Config)) {
return false;
}
// Confirm the configuration has been applied
if (!GetCommState(m_hCom, &m_Config)) {
return false;
}
// Clear the COM port input buffer
PurgeComm(m_hCom, PURGE_RXCLEAR);
return true;
}
// Initialize the COM port with default or specified parameters
bool Dad::cServer::Init(uint8_t NumPort, uint32_t QSPi_Size, DWORD BaudRate, BYTE ByteSize, BYTE Parity, BYTE StopBits) {
ResetClass();
// Initialize the buffer
if (QSPi_Size > QSPI_SIZE) {
return false;
}
m_QSPI_Size = QSPi_Size;
m_pBuff = new uint8_t[QSPi_Size];
if (nullptr == m_pBuff) {
return false;
}
memset(m_pBuff, 0, QSPi_Size);
m_pFile = (stFile*)m_pBuff;
m_pEndBuff = m_pBuff + QSPi_Size;
m_pFirstFreeBuff = m_pBuff + sizeof(Directory);
wchar_t NomPort[10];
swprintf_s(NomPort, 10, L"\\\\.\\COM%d", NumPort);
// Open the COM port
m_hCom = CreateFile(NomPort,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (m_hCom == INVALID_HANDLE_VALUE) {
return false;
}
// Initialize the DCB (Device Control Block) structure
SecureZeroMemory(&m_Config, sizeof(DCB));
m_Config.DCBlength = sizeof(DCB);
// Configure the COM port with the specified parameters
if (!Config(BaudRate, ByteSize, Parity, StopBits)) {
CloseHandle(m_hCom);
m_hCom = INVALID_HANDLE_VALUE;
return false;
}
COMMTIMEOUTS timeouts = { 0 };
// Set the timeouts for non-blocking reads
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.ReadTotalTimeoutMultiplier = 0;
if (!SetCommTimeouts(m_hCom, &timeouts)) {
CloseHandle(m_hCom);
m_hCom = INVALID_HANDLE_VALUE;
return false;
}
return true;
}
// Synchronize with the COM port and retrieve the block number
int16_t Dad::cServer::Synchronize() {
DWORD NbCharLus = 0;
uint8_t Buff;
int16_t NumBloc;
// Define the synchronization steps
enum class eStep {
Marker_B,
Marker_L,
Marker_O,
Marker_C,
NumBloc_1,
NumBloc_2
} Step = eStep::Marker_B;
// Get the start time in milliseconds
uint64_t startTime = GetTickCount64();
uint64_t timeout = 5000; // 5 seconds
while (GetTickCount64() - startTime < timeout) {
// Read one byte from the COM port
if (!ReadFile(m_hCom, &Buff, 1, &NbCharLus, NULL)) {
return -1;
}
if (1 == NbCharLus) {
// Perform the synchronization step
switch (Step) {
case eStep::Marker_B:
if (Buff == 'B') {
Step = eStep::Marker_L;
}
break;
case eStep::Marker_L:
if (Buff == 'L') {
Step = eStep::Marker_O;
}
else {
Step = eStep::Marker_B;
}
break;
case eStep::Marker_O:
if (Buff == 'O') {
Step = eStep::Marker_C;
}
else {
Step = eStep::Marker_B;
}
break;
case eStep::Marker_C:
if (Buff == 'C') {
Step = eStep::NumBloc_1;
}
else {
Step = eStep::Marker_B;
}
break;
case eStep::NumBloc_1:
NumBloc = Buff;
Step = eStep::NumBloc_2;
break;
case eStep::NumBloc_2:
NumBloc += (Buff << 8);
return NumBloc;
}
}
}
return -1;
}
// Transmit a block of data via the COM port
bool Dad::cServer::TransBloc(uint16_t NumBloc, uint8_t EndTrans) {
// Clear the COM port input buffer
PurgeComm(m_hCom, PURGE_RXCLEAR);
uint8_t* pData = m_pBuff + (NumBloc * TRANS_BLOCK_SIZE);
// Copy the data block and calculate the CRC
uint8_t* pBlocData = m_Bloc.Data;
uint8_t CalcCRC = 0;
for (uint16_t Index = 0; Index < TRANS_BLOCK_SIZE; Index++) {
CalcCRC += *pData;
*pBlocData++ = *pData++;
}
// Set the other fields of the block
m_Bloc.NumBloc = NumBloc;
m_Bloc._EndTrans = EndTrans;
m_Bloc._CRC = CalcCRC;
DWORD NbCharWrite = 0;
// Write the block to the COM port
if (!WriteFile(m_hCom, &m_Bloc, sizeof(Dad::Bloc), &NbCharWrite, NULL) ||
NbCharWrite != sizeof(Dad::Bloc)) {
return false;
}
return true;
}
// Add a file to the transfer buffer
bool Dad::cServer::addFile(const std::string& filePath, const std::string& fileName) {
if (m_IndexFile >= DIR_FILE_COUNT) {
return false;
}
if (true == isImageFile(fileName)) {
return addImageFile(filePath, fileName);
}else {
return addCommonFile(filePath, fileName);
}
}
// Add an image file to the transfer buffer(supports all GDI + formats including animated GIFs)
bool Dad::cServer::addImageFile(const std::string & filePath, const std::string & fileName) {
// Initialize GDI+
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
if (Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) != Gdiplus::Ok) {
return false;
}
bool result = false;
// Load the image
std::wstring wFilePath(filePath.begin(), filePath.end());
Gdiplus::Image* pImage = new Gdiplus::Image(wFilePath.c_str());
if (pImage->GetLastStatus() != Gdiplus::Ok) {
delete pImage;
Gdiplus::GdiplusShutdown(gdiplusToken);
return false;
}
// Check if it's an animated image (GIF, WebP, etc.)
UINT frameCount = pImage->GetFrameCount(&Gdiplus::FrameDimensionTime);
if (frameCount > 1) {
result = addAnimatedImageFile(pImage, fileName);
}
else {
result = addSingleImageFile(pImage, fileName);
}
delete pImage;
Gdiplus::GdiplusShutdown(gdiplusToken);
return result;
}
// Handle single static image using GDI+
bool Dad::cServer::addSingleImageFile(Gdiplus::Image* pImage, const std::string& fileName) {
// Get image dimensions
int width = pImage->GetWidth();
int height = pImage->GetHeight();
// Check for free space in buffer (RGBA format)
DWORD imageSize = 16 + (width * height * 4); // Image Information + RGBA format
if ((m_pFirstFreeBuff + imageSize) > m_pEndBuff) {
return false;
}
// Create file entry in directory
strncpy_s(m_pFile->Name, fileName.c_str(), MAX_ENTRY_NAME - 1);
m_pFile->Size = imageSize;
m_pFile->DataAddress = QSPI_ADRESSE + (m_pFirstFreeBuff - m_pBuff);
// Convert and copy image data to buffer
if (!copyGdiPlusImageToBuffer(pImage, width, height)) {
return false;
}
// Add Image Information
// Magic number
*m_pFirstFreeBuff++ = 'I';
*m_pFirstFreeBuff++ = 'M';
*m_pFirstFreeBuff++ = 'A';
*m_pFirstFreeBuff++ = 'G';
// Frame Count
*m_pFirstFreeBuff++ = 1; // Single frame
*m_pFirstFreeBuff++ = 0;
*m_pFirstFreeBuff++ = 0;
*m_pFirstFreeBuff++ = 0;
// Width
*m_pFirstFreeBuff++ = (width & 0xFF);
*m_pFirstFreeBuff++ = (width >> 8) & 0xFF;
*m_pFirstFreeBuff++ = (width >> 16) & 0xFF;
*m_pFirstFreeBuff++ = (width >> 24) & 0xFF;
// HEIGHT
*m_pFirstFreeBuff++ = (height & 0xFF);
*m_pFirstFreeBuff++ = (height >> 8) & 0xFF;
*m_pFirstFreeBuff++ = (height >> 16) & 0xFF;
*m_pFirstFreeBuff++ = (height >> 24) & 0xFF;
// Increment pointers
m_pFile++;
return true;
}
// Handle animated image file (GIF, animated WebP, etc.)
bool Dad::cServer::addAnimatedImageFile(Gdiplus::Image* pImage, const std::string& fileName) {
// Get frame count and dimensions
UINT frameCount = pImage->GetFrameCount(&Gdiplus::FrameDimensionTime);
int width = pImage->GetWidth();
int height = pImage->GetHeight();
// Calculate total size needed (all frames concatenated)
DWORD totalSize = 16 + (width * height * 4 * frameCount); // Image Information + RGBA format
// Check for free space in buffer
if ((m_pFirstFreeBuff + totalSize) > m_pEndBuff) {
return false;
}
// Create file entry in directory
strncpy_s(m_pFile->Name, fileName.c_str(), MAX_ENTRY_NAME - 1);
m_pFile->Size = totalSize;
m_pFile->DataAddress = QSPI_ADRESSE + (m_pFirstFreeBuff - m_pBuff);
// Extract and concatenate all frames
for (UINT frame = 0; frame < frameCount; frame++) {
// Select current frame
pImage->SelectActiveFrame(&Gdiplus::FrameDimensionTime, frame);
// Convert and copy frame data to buffer
if (!copyGdiPlusImageToBuffer(pImage, width, height)) {
return false;
}
}
// Add Image Information
// Magic number
*m_pFirstFreeBuff++ = 'I';
*m_pFirstFreeBuff++ = 'M';
*m_pFirstFreeBuff++ = 'A';
*m_pFirstFreeBuff++ = 'G';
// Frame Count
*m_pFirstFreeBuff++ = (frameCount & 0xFF);
*m_pFirstFreeBuff++ = (frameCount >> 8) & 0xFF;
*m_pFirstFreeBuff++ = (frameCount >> 16) & 0xFF;
*m_pFirstFreeBuff++ = (frameCount >> 24) & 0xFF;
// Width
*m_pFirstFreeBuff++ = (width & 0xFF);
*m_pFirstFreeBuff++ = (width >> 8) & 0xFF;
*m_pFirstFreeBuff++ = (width >> 16) & 0xFF;
*m_pFirstFreeBuff++ = (width >> 24) & 0xFF;
// HEIGHT
*m_pFirstFreeBuff++ = (height & 0xFF);
*m_pFirstFreeBuff++ = (height >> 8) & 0xFF;
*m_pFirstFreeBuff++ = (height >> 16) & 0xFF;
*m_pFirstFreeBuff++ = (height >> 24) & 0xFF;
// Increment file pointer
m_pFile++;
return true;
}
// Add a common file to the transfer buffer
bool Dad::cServer::addCommonFile(const std::string& filePath, const std::string& fileName) {
// Open the file
std::ifstream file(filePath, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
return false;
}
// Get file size
std::streamsize fileSize = file.tellg();
file.seekg(0, std::ios::beg);
// Check for free space in buffer
if ((m_pFirstFreeBuff + fileSize) > m_pEndBuff) {
file.close();
return false;
}
// Copy file data to buffer
if (!file.read((char*)m_pFirstFreeBuff, fileSize)) {
file.close();
return false;
}
// Create file entry in directory
strncpy_s(m_pFile->Name, fileName.c_str(), MAX_ENTRY_NAME - 1);
m_pFile->Size = fileSize;
m_pFile->DataAddress = QSPI_ADRESSE + (m_pFirstFreeBuff - m_pBuff);
// Increment pointers
m_pFirstFreeBuff += fileSize;
// Align to 4 bytes
m_pFirstFreeBuff = (uint8_t*)(((uintptr_t)m_pFirstFreeBuff + 3) & ~3);
m_pFile++;
file.close();
return true;
}
// Test if file is image file
bool Dad::cServer::isImageFile(const std::string& FileName) {
// List of valid extensions
const std::string validExtensions[] = { ".png", ".jpg", ".jpeg", ".bmp", ".tif", ".tiff", ".gif" };
// Find the position of the last dot (.)
size_t dotPosition = FileName.find_last_of('.');
if (dotPosition == std::string::npos) {
return false; // No extension found
}
// Extract the file extension and convert it to lowercase
std::string extension = FileName.substr(dotPosition);
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
// Check if the extension matches any in the list of valid extensions
for (const auto& validExtension : validExtensions) {
if (extension == validExtension) {
return true; // Valid image file
}
}
return false; // Not a valid image file
}
// Helper function to copy GDI+ image data to buffer
bool Dad::cServer::copyGdiPlusImageToBuffer(Gdiplus::Image* pImage, int width, int height) {
// Create a bitmap from the image
Gdiplus::Bitmap* pBitmap = new Gdiplus::Bitmap(width, height, PixelFormat32bppARGB);
if (pBitmap->GetLastStatus() != Gdiplus::Ok) {
delete pBitmap;
return false;
}
// Draw image onto bitmap
Gdiplus::Graphics graphics(pBitmap);
graphics.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
graphics.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
graphics.SetPixelOffsetMode(Gdiplus::PixelOffsetModeHighQuality);
if (graphics.DrawImage(pImage, 0, 0, width, height) != Gdiplus::Ok) {
delete pBitmap;
return false;
}
// Lock bitmap data
Gdiplus::BitmapData bitmapData;
Gdiplus::Rect rect(0, 0, width, height);
if (pBitmap->LockBits(&rect, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, &bitmapData) != Gdiplus::Ok) {
delete pBitmap;
return false;
}
// Copy bitmap data to buffer
BYTE* pSrc = (BYTE*)bitmapData.Scan0;
for (int y = 0; y < height; y++) {
BYTE* pSrcRow = pSrc + (y * bitmapData.Stride);
for (int x = 0; x < width; x++) {
// GDI+ uses BGRA format, convert to RGBA
*m_pFirstFreeBuff++ = pSrcRow[0]; // R
*m_pFirstFreeBuff++ = pSrcRow[1]; // G
*m_pFirstFreeBuff++ = pSrcRow[2]; // B
*m_pFirstFreeBuff++ = pSrcRow[3]; // A
pSrcRow += 4;
}
}
// Unlock and cleanup
pBitmap->UnlockBits(&bitmapData);
delete pBitmap;
return true;
}