Products > Test Equipment
Fluke undocumented image format "FBRLE2D" (now understand.)
squadchannel:
"FBRLE2D" is an image format that can be get in hardcopy.
It is used in the Fluke 190 Series II and Tek THS3000.
Interpreting the name:
Fluke Bitmap Run-Length Encoding 2D
as i think.
trying to understand this format.
I am trying to decompile and interpret the Fluke Scopemeter software (SW90W), but I don't have much experience with decompiling, interpret it to some extent in Ghidra, but when I rewrite it to work, and run it in C it does not result in a meaningful image file.
the software has a menu that allows you to retrieve the image remotely. After retrieving it, it seems to display it in a form in the GUI.
program itself seems to be native VB6 code. The DLL being called seems to be compiled in VB5.
I believe that ImgRleToFv in fv90img.dll is the function related to RLE decoding.
Is something being done in the function before this function is called,
or is it possible to make it into image data with the function after this,
or am I not understanding it well enough?
If you know, please help me.
Decompiled function. Edited to be understandable. Not sure if it is correct.:
--- Code: ---int ImgRleToFv(int rledata, undefined4 rletofv_param_2, double *image_area_global_handler, undefined4 *global_var_handler, double *rletofv_param_5)
{
int ret;
int ret_err;
float fVar1;
ret = checkFBRLE2Dheader(rledata, 0, 8);
if (ret == 0) { // if FBRLE2D isnt detect
ret_err = 0;
}else{ //FBRLE2D detect
ret = decode_header_area(rledata, 0xf, 0x120, global_var_handler);
if (ret == 0) {
ret_err = 0;
}else{
ret = decode_image_area?(rledata,0x24f,image_area_global_handler);
if (ret == 0) {
ret_err = 0;
}else{
/* float value return.but what? */
fVar1 = (float10)rlecall#4(rledata);
*rletofv_param_5 = (double)fVar1;
ret_err = 1;
}
}
}
return ret_err;
}
int checkFBRLE2Dheader(int rledata, int start_addr, int header_size)
{
char *rledata_pos;
char *header_name;
rledata_pos = (char *)(rledata + start_addr);
header_name = "FBRLE2D"; //header name
int break_flag = 1;
int loop_counter = 0;
while((loop_counter < header_size && (break_flag == 1))){
char rledata_pos_buf = *rledata_pos;
char header_name_buf = *header_name;
rledata_pos_buf = bin_curr_pos + 1;
header_name_buf = header_name + 1;
if (rledata_pos_buf != header_name_buf) {
break_flag = 0;
}
loop_counter = loop_counter + 1;
}
return break_flag;
}
int decode_header_area(int rledata,int headersize?, undefined4 imagestart_addr?, undefined4 *global_var_handler)
{
short ret_err;
undefined4 ret;
byte *rledata_pos;
int loop_cnt;
int *p_decode_buf;
/* GetElemsAlloc(dynamic_mem_handler, alloc_block_size, block_bytes_size,mode_flag?) */
ret_err = GenElemsAlloc(global_var_handler, 0x100, 4, 0);
if (ret_err == 0) {
ret_err = GenElemsAlloc(global_var_handler + 1,0x100,4,0);
if (ret_err == 0) {
rledata_pos = (byte *)(rledata + headersize?);
/* GenElemsLock = GlobalLock */
p_decode_buf = (int *)GenElemsLock(*global_var_handler);
for (loop_cnt = 0; loop_cnt < 0x60; loop_cnt = loop_cnt + 1) {
*p_decode_buf = (uint)*rledata_pos * 0x10000 + (uint)rledata_pos[1] * 0x100 + (uint)rledata_pos[2];
p_decode_buf = p_decode_buf + 1; //move 4byte(int)
rledata_pos = rledata_pos + 3; //move 1byte(char)
}
for (loop_cnt = 0x60; loop_cnt < 0x100; loop_cnt = loop_cnt + 1) {
*decode_buf = 0;
decode_buf = decode_buf + 1;
}
GenElemsUnlock(*global_var_handler);
decode_buf = (int *)GenElemsLock(global_var_handler[1]);
for (loop_cnt = 0; loop_cnt < 0x60; loop_cnt = loop_cnt + 1) {
*decode_buf = (uint)*rledata_pos * 0x10000 + (uint)rledata_pos[1] * 0x100 +
(uint)rledata_pos[2];
decode_buf = decode_buf + 1;
rledata_pos = rledata_pos + 3;
}
for (loop_cnt = 0x60; loop_cnt < 0x100; loop_cnt = loop_cnt + 1) {
*decode_buf = 0;
decode_buf = decode_buf + 1;
}
GenElemsUnlock(global_var_handler[1]);
ret = 1;
}else{
GenElemsFree(global_var_handler);
ret = 0;
}
}else{
/* return */
ret = 0;
}
return ret;
}
int decode_image_area?(int rledata,int img_data_start_addr,double *global_var_handler)
{
int ret_err;
int ret;
char *buf?;
char *rledata_pos;
char *p_decode_buf;
int rledata_pos_buf;
int rledata[1]_byte_buf;
int horiz_cnt;
int vert_cnt;
ret_err = GenElemsAlloc(global_var_handler,0x12c00,1,0);
if (ret_err == 0) {
rledata_pos = (byte *)(rledata + img_data_start_addr);
p_decode_buf = (byte *)GenElemsLock(global_var_handler);
for (vert_cnt = 0; vert_cnt < 0xf0; vert_cnt = vert_cnt + 1) {
horiz_cnt = 0;
while (horiz_cnt < 0x140) {
if ((*rledata_pos & 0x80) == 0) { //bit 8 not flagged
*p_decode_buf = *rledata_pos;
p_decode_buf = p_decode_buf + 1; //move 1byte(char)
rledata_pos = rledata_pos + 1; //move 1byte(char)
horiz_cnt = horiz_cnt + 1;
}else{ //bit 8 is flagged
rledata_pos_buf = *rledata_pos;
rledata[1]_pos_buf = rledata_pos[1] + 2;
rledata_pos = rledata_pos + 2;
if ((rledata_pos_buf & 0x7f) < 0x60) {
for (; curr_rledata[1]_byte_buf != 0; curr_rledata[1]_byte_buf = curr_rledata[1]_byte_buf + -1){
*encode_buf = curr_rledata_byte_buf & 0x7f;
encode_buf = encode_buf + 1;
horiz_cnt = horiz_cnt + 1;
}
}else{
buf? = p_decode_buf + (0x80 - (rledata_pos_buf & 0x7f)) * -0x140;
for (; rledata[1]_pos_buf != 0; rledata[1]_pos_buf = rledata[1]_pos_buf + -1){
*p_decode_buf = *buf?;
p_decode_buf = p_decode_buf + 1;
buf? = buf? + 1;
horiz_cnt = horiz_cnt + 1;
}
}
}
}
}
GenElemsUnlock(global_var_handler);
ret = 1;
}else{
ret = 0;
}
return ret;
}
int GenElemsAlloc(int *dynamic_mem_handler,int allocation_block,short block_size_byte?,short mode_flag?)
{
HGLOBAL allocd_mem_handle;
int alloc_mem_size_bytes;
int ret;
int dynamic_mem_size;
if (allocation_block < 1) {
allocation_block = 1;
}
ret = allocation_block * block_size_byte?;
dynamic_mem_size = ret;
if ((ret - 1 & ret) != 0) {
for (dynamic_mem_size = 1;
((int)dynamic_mem_size < (int)ret && ((int)dynamic_mem_size < 0x40000000));
dynamic_mem_size = dynamic_mem_size << 1) {
}
if ((int)dynamic_mem_size < (int)ret) {
return CONCAT22((short)(dynamic_mem_size >> 0x10),0xffff);
}
}
if ((mode_flag? == 0) || (*dynamic_mem_handler == 0)) {
if (*dynamic_mem_handler != 0) {
alloc_mem_size_bytes = GlobalSize((HGLOBAL)*dynamic_mem_handler);
if (alloc_mem_size_bytes == dynamic_mem_size) {
return alloc_mem_size_bytes & 0xffff0000;
}
GenElemsFree(dynamic_mem_handler);
}
/* GlobalAlloc(GMEM_MOVEABLE, dwBytes) */
allocd_mem_handle = GlobalAlloc(2,dynamic_mem_size);
}else{
allocd_mem_handle = GlobalReAlloc((HGLOBAL)*dynamic_mem_handler,dynamic_mem_size,2);
}
if (allocd_mem_handle == (HGLOBAL)0x0) {
/* error cant allcation mem. return 65535 */
ret = 0xffff;
}else{
/* sucsessfull allocation mem. set handle pointer and return */
*dynamic_mem_handler = (int)allocd_mem_handle;
ret = (uint)allocd_mem_handle & 0xffff0000;
}
return ret;
}
void GenElemsLock(undefined4 *param_1)
{
/* 0x437c 49 GenElemsLock */
GlobalLock((HGLOBAL)*param_1);
return;
}
void GenElemsUnlock(undefined4 *param_1)
{
/* 0x438f 52 GenElemsUnlock */
GlobalUnlock((HGLOBAL)*param_1);
return;
}
void GenElemsFree(int *param_1)
{
/* 0x4358 48 GenElemsFree */
if (*param_1 != 0) {
GlobalFree((HGLOBAL)*param_1);
*param_1 = 0;
}
return;
}
--- End code ---
1)checking to see if “FBRLE2D” is present.
2)number specified in hex is a meaningful number compared to the RLE-encoded file.
3)values for horiz_cnt and vert_cnt are exactly 320x240.
Also, there are arguments that are not used in the "decode_image_area?" function.
I am wondering if ghidra is not decompiling enough.
attached bmp is saved using the scope's USB save function.
I believe it is the same image as the RLE encoded file because I saved it under the same conditions and with the same screen display content.
(Maybe it is not...)
Can't ghidra project data be shared?
I have been thinking about it for the past few days, until midnight as if I was being chased, and decided to ask someone.
squadchannel:
hmmm... it is close, but incomplete. :palm:
definitely a function being called in addition to ImgRleToFv.
squadchannel:
ImgRleToFv function is apparently a project file, data used inside the software.
modding the project file to what I decoded image.
close! very close! At least the decoding seems to be working. Still had bug, though. :phew:
parawizard:
Very interesting. I figured out FV SW860 which is just a 1-bit bit map. Monochrome screen of course so it's much simpler. Is this a screen capture? I found that QUERY WAVEFORM actually sends enough information to recreate the screen with each point having a resolution of 0-256ish. Can that version of Flukeview mirror the screen from the meter? Or is it a save screen BMP?
squadchannel:
using FlukeView 5.4
When I do a screen capture, the QP command is executed and the image data is retrieved.
Not sure about other models, but the 190II and THS3000 are encoded in FBRLE2D and sent to the software.
190 supports Epson Compatible(ESC/P?), LaserJet, DeskJet, PostScript.
190C supports Epson Compatible(ESC/P?), PNG, and FBRLE2D.
190II supports Epson Compatible(ESC/P?), and FBRLE2D.
THS3000 is 190II based hardware, but for some reason is limited to only FBRLE2D.
i am using THS3024.
of cource, does not work THS3024 with Flukeview, flashed the 190II firmware.
perfect image on the right was captured by the scope's capture function and saved to USB.
Navigation
[0] Message Index
[#] Next page
Go to full version