C语言之mot文件解析

        在嵌入式程序开发中,除了与hex文件接触得比较多外,也遇到过不少是用mot文件来进行程序烧写的。我们所说的mot文件实际上就是Motorola S-records文件,是摩托罗拉公司定义的一种S开头的数据记录文件格式。

如下图所示:

平时该格式文件通过专门的烧录工具进行程序烧写,无需知道里面数据的含义,但是当想对接串口、can口等外设进行程序升级时,我们就必须明白mot文件中数据的含义了,只有知道数据的含义,才能提取其关键的信息进行程序开发。

1.格式介绍:

.mot文件每行以‘S’字符开头,第二个字符为数字字符,决定了该行数据的类型,之后的数据为二进制数据转换为hex字符串形式表示,可视为字节数组。

2.数据解析

解析一行数据的代码可参考: https://blog.csdn.net/lin_strong/article/details/79448091

但是仔细分析数据后发现,多行数据其实是一个连续的数据块,将其按数据块解析,将会更加便于我们使用数据。

如下为自行编写的解析代码,若存在问题或bug请留言或与我联系,万分感谢~

mot_file_info.c

/**
 * @file mot_file_info.c
 * @brief  MOT文件数据解析模块
 * @author fangye ([email protected])
 * @date 2020-07-14
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mot_file_info.h"

static MOT_INFO_HEAD *s_mot_info_head = NULL;
static MOT_INFO_NODE *s_node_ptr = NULL;

/**
 * @brief 获取MOT文件链表信息指针
 * @return 链表头指针 
 */
MOT_INFO_HEAD * get_mot_info()
{
	return s_mot_info_head;
}

/**
 * @brief 初始化链表头
 */
static void init_mot_info_list()
{
	s_mot_info_head = (MOT_INFO_HEAD *)malloc(sizeof(MOT_INFO_HEAD));
	s_mot_info_head->run_addr = 0;
	s_mot_info_head->node_num = 0;
	s_mot_info_head->next = NULL;
}

/**
 * @brief 添加数据至链表中
 * 连续的数据块存储在同一节点上
 * @param  data_addr        数据写入地址
 * @param  data             数据内容
 * @param  len              数据长度
 * @return int 
 * - -1:失败
 * - 0:成功
 */
static int add_data_info_to_list(unsigned int data_addr, char *data, unsigned int len)
{
	if( data == NULL )
		return -1;

	if(s_mot_info_head == NULL)
		init_mot_info_list();

	if(s_mot_info_head->next == NULL)		//如果头节点为空
	{
		s_mot_info_head->next = (MOT_INFO_NODE *)malloc(sizeof(MOT_INFO_NODE));
		s_mot_info_head->next->write_addr = data_addr;
		s_mot_info_head->next->data_len = len;
		s_mot_info_head->next->data = (char *)malloc(len*sizeof(char));
		memcpy(s_mot_info_head->next->data, data, len);				//保存数据
		s_mot_info_head->next->next = NULL;
		s_mot_info_head->node_num++;		//节点数增加
	}
	else					//节点有数据
	{
		s_node_ptr = s_mot_info_head->next;
		while(s_node_ptr->next != NULL)
		{
			s_node_ptr = s_node_ptr->next;		//找到最新的数据节点
		}

		if(data_addr == s_node_ptr->write_addr + s_node_ptr->data_len)		//如果为连续数据
		{
			s_node_ptr->data = (char *)realloc(s_node_ptr->data, (s_node_ptr->data_len + len)*sizeof(char)); 	//重新申请内存
			memcpy(&s_node_ptr->data[s_node_ptr->data_len], data, len);	//拷贝数据至末尾
			s_node_ptr->data_len += len;
		}
		else //如果为不连续数据
		{	
			s_node_ptr->next = (MOT_INFO_NODE *)malloc(sizeof(MOT_INFO_NODE));
			if(s_node_ptr->next == NULL)
				printf("malloc next fail!\n");
			
			s_node_ptr = s_node_ptr->next;
			
			s_mot_info_head->node_num++;		//节点数增加
			s_node_ptr->write_addr = data_addr;
			s_node_ptr->data_len = len;
			s_node_ptr->data = (char *)malloc(s_node_ptr->data_len*sizeof(char));
			if(s_node_ptr->data == NULL)
				printf("malloc data fail!\n");
			
			memcpy(s_node_ptr->data, data, len);				///< 保存数据
			s_node_ptr->next = NULL;
		}
#if 0
		printf("data_len[%d]  addr[0x%08X]: ",len, data_addr);
		int i = 0;
		for(i = 0; i<len; i++)
		{
			printf("%02X ",(unsigned char)data[i]);
		}
		printf("\n");
#endif
	}
	return 0;
}

/**
 * @brief 添加运行信息至链表头
 * @param  run_addr         起始运行地址
 * @return int 
 */
static int add_run_info_to_head(unsigned int run_addr)
{
	if(s_mot_info_head == NULL)
		init_mot_info_list();
	
	s_mot_info_head->run_addr = run_addr;
	
	return 0;
}

/**
 * @brief 单字节hex字符转数字
 * @param  hex              hex字符
 * @return int 
 * - -1:失败
 * - >=0:成功
 */
static int CharToInt(char hex) 
{
    if (hex>='0' && hex <='9')
        return hex - '0';
    if (hex>='A' && hex <= 'F')
        return hex-'A'+10;
    if(hex>='a' && hex <= 'f')
        return hex-'a'+10;
    return -1;
}

/**
 * @brief 两字节hex字符串转数字
 * @param  hex              两字节hex字符串
 * @return int 
 * - >=0:成功
 * - <0:失败
 */
static int StringToInt(const char *hex)
{
	
	int high =  CharToInt(hex[0]);
	int low = CharToInt(hex[1]);
	if(high != -1 && low != -1)
		return high*0x10 + low;
	else
	{
		printf("---StringToInt error!! %s\n",hex);
		return -1;
	}
}

/**
 * @brief 字符串hex数据转字节数组
 * @param  mot_line_buff    Mot文件字符串数据
 * @param  type             数据类型
 * @param  data             转换后数据内容
 * @param  data_len         转换后数据内容长度
 * @return int 
 * - -1:转换失败
 * - 0:转换成功
 */
static int str2bytes(const char *mot_line_buff, int *type, char *data, int *data_len)
{
	if( mot_line_buff == NULL)
		return -1;

	int len = strlen(mot_line_buff);
	if(len %2 != 0)
		return -1;
	*type = mot_line_buff[1] - '0';

	int i = 0;
	int length = 0;
	
	len -= 2; ///< 减去换行符/r/n两个字节
	for(i = 2; i < len; i += 2 )
	{
		int ret  = StringToInt(&mot_line_buff[i]);
		if(ret >= 0)
		{
			data[length] = ret;
			length++;
		}
		else
		{
			return -1;
		}
	}
	*data_len = length;
	
	return 0;
}

/**
 * @brief 检测数据合法性
 * @param  data             数据内容
 * @param  len              数据长度
 * @return int 
 * - 0:非法
 * - 1:合法
 */
static int is_data_avaliable(unsigned char *data, int len)
{
	if(data == NULL)
		return 0;
	int i = 0;
	unsigned char add_sum = data[0];
	for(i=1; i<len-1; i++)
	{
		add_sum += data[i];
	}
	add_sum = ~add_sum;
	
	if(add_sum == data[len-1])
		return 1;		//校验通过
	else 
	{
		printf("add_sum = %d  data[%d]=%d\n",add_sum, len-1, data[len-1]);
		return 0;		//校验未通过
	}
}


/**
 * @brief 解析一行数据
 * @param  line_data        源数据内容
 * @param  line_data_len    源数据长度
 * @param  data_type        源数据类型
 * @param  addr             数据写入地址或运行地址
 * @param  bin_data         解析后的数据内容
 * @param  data_len         解析后的数据内容长度
 * @return int 
 * - 0:失败
 * - 非0:成功
 */
static int parse_a_line(char *line_data, int line_data_len, int data_type, unsigned int *addr, char *bin_data, int *data_len)
{
	if(line_data == NULL || addr == NULL || bin_data == NULL || data_len == NULL)
		return 0;
#if 0
	int i = 0;
	for(i = 0; i<line_data_len; i++)
	{
		printf("%02X ",(unsigned char)line_data[i]);
	}
	printf("\n");
#endif

	switch(data_type)
	{
		case 1:
		case 2:
		case 3:
		{
			int data_addr_byte_len = 1 + data_type;	///< 数据地址域长度
			int i = 0;
			for(int i=1; i<data_addr_byte_len; i++)
			{
				*addr += (unsigned char)line_data[i];
				*addr = (*addr) << 8;
				if(i+1 == data_addr_byte_len)
					*addr += (unsigned char)line_data[i+1];
			}
			//printf("-- data addr = %08X\n",*addr);
			*data_len = line_data_len - data_addr_byte_len - 2;
			memcpy(bin_data, &line_data[data_addr_byte_len+1], *data_len);
		}
		break;
		case 7:
		case 8:
		case 9:
		{
			int run_addr_byte_len = 11 - data_type;	//运行地址域长度
			int i = 0;
			for(int i=1; i<run_addr_byte_len; i++)
			{
				*addr += (unsigned char)line_data[i];
				*addr = (*addr) << 8;
				if(i+1 == run_addr_byte_len)
					*addr += (unsigned char)line_data[i+1];
			}
			//printf("-- run addr = %08X\n",*addr);
		}
		break;
		default:break;
	}
	return 1;
}

/**
 * @brief 释放所有节点
 * @param  node             数据块节点
 * @return int 
 */
static int free_all_node(MOT_INFO_NODE *node)
{
	if (node->next != NULL)
	{
		free_all_node(node->next);
	}
	free(node->data);
	node->data = NULL;

	free(node);
	node = NULL;
}

/**
 * @brief 释放mot信息链表
 * @param  header           链表头指针
 */
void free_mot_info_list(MOT_INFO_HEAD *header)
{
	if( header->next )
	{
		free_all_node(header->next);
	}
	free(header);
	header = NULL;
}

/**
 * @brief 加载mot文件信息
 * @param  mot_filepath     mot文件路径
 * @return int 
 * - 0: 成功
 * - -1:失败
 */
int load_mot_file(const char *mot_filepath)
{
	if( mot_filepath == NULL)
		return -1;

	FILE *fp = fopen(mot_filepath,"r");
	if(fp)
	{
		char buff[512] = {0};
		while( fgets(buff, sizeof(buff), fp) != NULL) //每次读取一行
		{
			char line_data[512]={0};
			int line_data_len = 0;
			int type = 0;
			if(0 == str2bytes(buff, &type, line_data, &line_data_len))	//转为数组
			{
				if( is_data_avaliable(line_data, line_data_len) )	//校验数据合法性
				{
					unsigned int addr = 0;
					char bin_data[512]={0};
					int data_len = 0;
					
					if(parse_a_line(line_data, line_data_len, type, &addr, bin_data, &data_len))//解析一行数据
					{
						if(type == 1 || type == 2 || type == 3)	//数据地址及数据内容
						{
							add_data_info_to_list(addr, bin_data, data_len);
						}
						else if(type == 7 || type == 8 || type == 9)  //运行地址
						{
							add_run_info_to_head(addr);
						}
					}
					else	//解析失败
					{
						printf("parse a line data fail!\n");
						return -1;
					}
				}
				else //数据校验失败
				{
					printf("mot data is error!\n");
					return -1;
				}
			}
			else	//转换为数组失败
			{
				printf("a line str2bytes error!\n");
				return -1;
			}
			memset(buff,0,sizeof(buff));
		}
		fclose(fp);
		fp = NULL;
	}
	else
	{
		printf("open mot file %s fail!\n",mot_filepath);
		return -1;
	}
	return 0;
}

mot_file_info.h

/**
 * @file mot_file_info.h
 * @brief .MOT文件数据解析模块头文件
 * @author fangye ([email protected])
 * @date 2020-07-14
 */

#ifndef __MOT_FILE_INFO_H__
#define __MOT_FILE_INFO_H__

#ifdef __cplusplus
extern "C"{
#endif /* __cplusplus */

/**
 * @brief mot文件数据块节点
 */
typedef struct _MOT_INFO_NODE
{
	unsigned int write_addr;			///< 数据写入地址
	unsigned int data_len;				///< 数据长度
	char *data;							///< 数据内容
	struct _MOT_INFO_NODE *next;		///< 下一段地址不连续的数据内容
}MOT_INFO_NODE;

/**
 * @brief mot文件信息链表头
 */
typedef struct _MOT_INFO_HEAD
{
	unsigned int run_addr;			///< 程序运行地址
	unsigned int node_num;			///< 地址段节点个数
	struct _MOT_INFO_NODE *next;	///< 链表头节点指针
}MOT_INFO_HEAD;

extern int load_mot_file(const char *mot_filepath);

extern MOT_INFO_HEAD *get_mot_info();

extern void free_mot_info_list(MOT_INFO_HEAD *header);

#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __MOT_FILE_INFO_H__ */

main.c

#include <stdio.h>
#include <string.h>
#include "mot_file_info.h"

int main(int argc, char *argv[])
{
	if(argc != 2)
		return 0;

	int ret = load_mot_file(argv[1]);
	if(ret == 0)
	{
		printf("load mot file success!!\n");
		MOT_INFO_HEAD *mot_info = get_mot_info();//获取mot文件信息
		printf("--- run_addr = 0x%08X  node_num = %d ---\n", mot_info->run_addr, mot_info->node_num);
		MOT_INFO_NODE *node_ptr = mot_info->next;
		if(node_ptr)
		{
			do
			{
				printf("write_addr = 0x%08X  data_len = 0x%08X\n", node_ptr->write_addr, node_ptr->data_len);
				node_ptr = node_ptr->next;
			}
			while(node_ptr);
		}
		free_mot_info_list(mot_info);	///< 释放链表
	}
	return 0;
}

执行结果:

如图所示上图所示:

能够直接链表头获取到整个程序运行的起始地址、不连续数据块个数 、每个数据块的大小和写入的起始地址。

猜你喜欢

转载自blog.csdn.net/fangye945a/article/details/107416616