647 lines
20 KiB
C
647 lines
20 KiB
C
/**
|
||
* @file blf.c
|
||
* @brief Реализация функций для создания BLF-файлов (разделяемая библиотека).
|
||
*/
|
||
|
||
#include "blf.h"
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <time.h>
|
||
|
||
/* -------------------------------------------------------------------------
|
||
* Упакованные структуры BLF
|
||
* ------------------------------------------------------------------------- */
|
||
#ifdef _MSC_VER
|
||
#pragma pack(push, 1)
|
||
#define PACKED_STRUCT
|
||
#else
|
||
#define PACKED_STRUCT __attribute__((packed))
|
||
#endif
|
||
|
||
typedef struct {
|
||
uint16_t year;
|
||
uint16_t month;
|
||
uint16_t dayOfWeek;
|
||
uint16_t day;
|
||
uint16_t hour;
|
||
uint16_t minute;
|
||
uint16_t second;
|
||
uint16_t milliseconds;
|
||
} PACKED_STRUCT SYSTEMTIME_PACKED;
|
||
|
||
typedef struct {
|
||
uint32_t signature;
|
||
uint32_t statisticsSize;
|
||
uint32_t apiNumber;
|
||
uint8_t applicationId;
|
||
uint8_t compressionLevel;
|
||
uint8_t applicationMajor;
|
||
uint8_t applicationMinor;
|
||
uint64_t fileSize;
|
||
uint64_t uncompressedFileSize;
|
||
uint32_t objectCount;
|
||
uint32_t applicationBuild;
|
||
SYSTEMTIME_PACKED measurementStartTime;
|
||
SYSTEMTIME_PACKED lastObjectTime;
|
||
uint64_t restorePointsOffset;
|
||
uint32_t reservedFileStatistics[16];
|
||
} PACKED_STRUCT FileHeader;
|
||
|
||
typedef struct {
|
||
uint32_t mSignature;
|
||
uint16_t mHeaderSize;
|
||
uint16_t mHeaderVersion;
|
||
uint32_t mObjectSize;
|
||
uint32_t mObjectType;
|
||
} PACKED_STRUCT VBLObjectHeaderBase;
|
||
|
||
typedef struct {
|
||
VBLObjectHeaderBase mBase;
|
||
uint32_t mObjectFlags;
|
||
uint16_t mClientIndex;
|
||
uint16_t mObjectVersion;
|
||
uint64_t mObjectTimeStamp;
|
||
} PACKED_STRUCT VBLObjectHeader;
|
||
|
||
typedef struct {
|
||
uint16_t mChannel;
|
||
uint8_t mFlags;
|
||
uint8_t mDLC;
|
||
uint32_t mID;
|
||
uint8_t mData[8];
|
||
} PACKED_STRUCT VBLCANMessage;
|
||
|
||
typedef struct {
|
||
uint16_t mChannel;
|
||
uint8_t mID;
|
||
uint8_t mDLC;
|
||
uint8_t mData[8];
|
||
uint8_t mFSMId;
|
||
uint8_t mFSMState;
|
||
uint8_t mHeaderTime;
|
||
uint8_t mFullTime;
|
||
uint16_t mCRC;
|
||
uint8_t mDir;
|
||
uint8_t mReserved;
|
||
} PACKED_STRUCT VBLLINMessage;
|
||
|
||
typedef struct {
|
||
uint16_t mChannel;
|
||
uint8_t mID;
|
||
uint8_t mDLC;
|
||
uint8_t mFSMId;
|
||
uint8_t mFSMState;
|
||
uint8_t mHeaderTime;
|
||
uint8_t mFullTime;
|
||
} PACKED_STRUCT VBLLINSendError;
|
||
|
||
typedef struct {
|
||
uint16_t compressionMethod;
|
||
uint16_t reserved1;
|
||
uint32_t reserved2;
|
||
uint32_t uncompressedFileSize;
|
||
uint32_t reserved3;
|
||
} PACKED_STRUCT LogContainerData;
|
||
|
||
typedef struct {
|
||
VBLObjectHeaderBase base;
|
||
LogContainerData data;
|
||
} PACKED_STRUCT ContainerHeader;
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma pack(pop)
|
||
#endif
|
||
|
||
/* -------------------------------------------------------------------------
|
||
* Контекст (полное определение)
|
||
* ------------------------------------------------------------------------- */
|
||
typedef struct BLFContext {
|
||
FILE* fp;
|
||
FileHeader header;
|
||
long headerPos;
|
||
int objectCount;
|
||
uint64_t maxTimestamp;
|
||
int in_container;
|
||
ContainerHeader container_hdr;
|
||
long container_hdr_pos;
|
||
uint64_t container_timestamp;
|
||
} BLFContext;
|
||
|
||
/* -------------------------------------------------------------------------
|
||
* Вспомогательные функции
|
||
* ------------------------------------------------------------------------- */
|
||
static int systemtime_to_packed(const SYSTEMTIME* src, SYSTEMTIME_PACKED* dst) {
|
||
if (!src || !dst) return -1;
|
||
dst->year = src->year;
|
||
dst->month = src->month;
|
||
dst->dayOfWeek = src->dayOfWeek;
|
||
dst->day = src->day;
|
||
dst->hour = src->hour;
|
||
dst->minute = src->minute;
|
||
dst->second = src->second;
|
||
dst->milliseconds = src->milliseconds;
|
||
return 0;
|
||
}
|
||
|
||
static void systemtime_packed_to_unpacked(const SYSTEMTIME_PACKED* src, SYSTEMTIME* dst) {
|
||
dst->year = src->year;
|
||
dst->month = src->month;
|
||
dst->dayOfWeek = src->dayOfWeek;
|
||
dst->day = src->day;
|
||
dst->hour = src->hour;
|
||
dst->minute = src->minute;
|
||
dst->second = src->second;
|
||
dst->milliseconds = src->milliseconds;
|
||
}
|
||
|
||
static void systemtime_add_ms(SYSTEMTIME_PACKED* st, uint64_t ms) {
|
||
uint64_t total_ms = st->milliseconds + ms;
|
||
st->milliseconds = (uint16_t)(total_ms % 1000);
|
||
uint64_t carry_seconds = total_ms / 1000;
|
||
|
||
uint64_t total_seconds = st->second + carry_seconds;
|
||
st->second = (uint16_t)(total_seconds % 60);
|
||
uint64_t carry_minutes = total_seconds / 60;
|
||
|
||
uint64_t total_minutes = st->minute + carry_minutes;
|
||
st->minute = (uint16_t)(total_minutes % 60);
|
||
uint64_t carry_hours = total_minutes / 60;
|
||
|
||
uint64_t total_hours = st->hour + carry_hours;
|
||
st->hour = (uint16_t)(total_hours % 24);
|
||
/* Дни не меняем – считаем, что запись укладывается в один день */
|
||
}
|
||
|
||
static void get_current_systemtime(SYSTEMTIME* st) {
|
||
time_t t = time(NULL);
|
||
struct tm* tm = localtime(&t);
|
||
st->year = tm->tm_year + 1900;
|
||
st->month = tm->tm_mon + 1;
|
||
st->dayOfWeek = tm->tm_wday;
|
||
st->day = tm->tm_mday;
|
||
st->hour = tm->tm_hour;
|
||
st->minute = tm->tm_min;
|
||
st->second = tm->tm_sec;
|
||
st->milliseconds = 0; /* точность до секунды */
|
||
}
|
||
|
||
/* -------------------------------------------------------------------------
|
||
* Файловые операции (только stdio)
|
||
* ------------------------------------------------------------------------- */
|
||
static int blf_file_write(FILE* fp, const void* ptr, size_t size) {
|
||
if (fwrite(ptr, 1, size, fp) != size) {
|
||
fprintf(stderr, "ERROR: fwrite failed (size=%zu)\n", size);
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static long blf_file_tell(FILE* fp) {
|
||
long pos = ftell(fp);
|
||
if (pos < 0) {
|
||
fprintf(stderr, "ERROR: ftell failed\n");
|
||
}
|
||
return pos;
|
||
}
|
||
|
||
static int blf_file_seek(FILE* fp, long offset) {
|
||
if (fseek(fp, offset, SEEK_SET) != 0) {
|
||
fprintf(stderr, "ERROR: fseek to %ld failed\n", offset);
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int blf_file_seek_end(FILE* fp) {
|
||
if (fseek(fp, 0, SEEK_END) != 0) {
|
||
fprintf(stderr, "ERROR: fseek to end failed\n");
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* -------------------------------------------------------------------------
|
||
* Реализация API
|
||
* ------------------------------------------------------------------------- */
|
||
BLF_API void* blf_open(const char* filename, const SYSTEMTIME* startTime) {
|
||
BLFContext* ctx = (BLFContext*)calloc(1, sizeof(BLFContext));
|
||
if (!ctx) {
|
||
fprintf(stderr, "ERROR: Failed to allocate context\n");
|
||
return NULL;
|
||
}
|
||
|
||
ctx->fp = fopen(filename, "w+b");
|
||
if (!ctx->fp) {
|
||
fprintf(stderr, "ERROR: Failed to open file %s\n", filename);
|
||
free(ctx);
|
||
return NULL;
|
||
}
|
||
|
||
/* Инициализация заголовка */
|
||
ctx->header.signature = BL_FILE_SIGNATURE;
|
||
ctx->header.statisticsSize = sizeof(FileHeader);
|
||
ctx->header.apiNumber = 4070100;
|
||
ctx->header.applicationId = 0;
|
||
ctx->header.compressionLevel = 0;
|
||
ctx->header.applicationMajor = 0;
|
||
ctx->header.applicationMinor = 0;
|
||
ctx->header.applicationBuild = 0;
|
||
ctx->header.fileSize = 0;
|
||
ctx->header.uncompressedFileSize = 0;
|
||
ctx->header.objectCount = 0;
|
||
ctx->header.restorePointsOffset = 0;
|
||
|
||
/* Время начала */
|
||
SYSTEMTIME st;
|
||
if (startTime) {
|
||
st = *startTime;
|
||
} else {
|
||
get_current_systemtime(&st);
|
||
}
|
||
if (systemtime_to_packed(&st, &ctx->header.measurementStartTime) != 0) {
|
||
fclose(ctx->fp);
|
||
free(ctx);
|
||
return NULL;
|
||
}
|
||
/* lastObjectTime пока равно startTime */
|
||
ctx->header.lastObjectTime = ctx->header.measurementStartTime;
|
||
|
||
/* Запись заголовка */
|
||
ctx->headerPos = blf_file_tell(ctx->fp);
|
||
if (ctx->headerPos < 0) {
|
||
fclose(ctx->fp);
|
||
free(ctx);
|
||
return NULL;
|
||
}
|
||
if (blf_file_write(ctx->fp, &ctx->header, sizeof(FileHeader)) != 0) {
|
||
fclose(ctx->fp);
|
||
free(ctx);
|
||
return NULL;
|
||
}
|
||
|
||
ctx->objectCount = 0;
|
||
ctx->maxTimestamp = 0;
|
||
ctx->in_container = 0;
|
||
return ctx;
|
||
}
|
||
|
||
BLF_API int blf_set_start_time(void* handle, const SYSTEMTIME* startTime) {
|
||
if (!handle || !startTime) return -1;
|
||
BLFContext* ctx = (BLFContext*)handle;
|
||
return systemtime_to_packed(startTime, &ctx->header.measurementStartTime);
|
||
}
|
||
|
||
BLF_API int blf_start_container(void* handle, uint64_t timestamp) {
|
||
if (!handle) return -1;
|
||
BLFContext* ctx = (BLFContext*)handle;
|
||
if (ctx->in_container) {
|
||
fprintf(stderr, "ERROR: Already inside a container\n");
|
||
return -1;
|
||
}
|
||
|
||
/* Подготовка заголовка контейнера */
|
||
ctx->container_hdr.base.mSignature = BL_OBJ_SIGNATURE;
|
||
ctx->container_hdr.base.mHeaderSize = sizeof(VBLObjectHeaderBase);
|
||
ctx->container_hdr.base.mHeaderVersion = 1;
|
||
ctx->container_hdr.base.mObjectSize = 0; /* будет заполнено позже */
|
||
ctx->container_hdr.base.mObjectType = BL_OBJ_TYPE_LOG_CONTAINER;
|
||
|
||
memset(&ctx->container_hdr.data, 0, sizeof(LogContainerData));
|
||
ctx->container_hdr.data.compressionMethod = 0;
|
||
|
||
/* Запись заголовка контейнера */
|
||
ctx->container_hdr_pos = blf_file_tell(ctx->fp);
|
||
if (ctx->container_hdr_pos < 0) return -1;
|
||
if (blf_file_write(ctx->fp, &ctx->container_hdr, sizeof(ContainerHeader)) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to write container header\n");
|
||
return -1;
|
||
}
|
||
|
||
ctx->container_timestamp = timestamp;
|
||
ctx->in_container = 1;
|
||
return 0;
|
||
}
|
||
|
||
BLF_API int blf_end_container(void* handle) {
|
||
if (!handle) return -1;
|
||
BLFContext* ctx = (BLFContext*)handle;
|
||
if (!ctx->in_container) {
|
||
fprintf(stderr, "ERROR: No open container to end\n");
|
||
return -1;
|
||
}
|
||
|
||
long current_pos = blf_file_tell(ctx->fp);
|
||
if (current_pos < 0) return -1;
|
||
long data_size = current_pos - (ctx->container_hdr_pos + sizeof(ContainerHeader));
|
||
if (data_size < 0) {
|
||
fprintf(stderr, "ERROR: Negative container data size\n");
|
||
return -1;
|
||
}
|
||
|
||
ctx->container_hdr.base.mObjectSize = sizeof(ContainerHeader) + data_size;
|
||
ctx->container_hdr.data.uncompressedFileSize = (uint32_t)data_size;
|
||
|
||
if (blf_file_seek(ctx->fp, ctx->container_hdr_pos) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to seek to container header\n");
|
||
return -1;
|
||
}
|
||
if (blf_file_write(ctx->fp, &ctx->container_hdr, sizeof(ContainerHeader)) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to rewrite container header\n");
|
||
return -1;
|
||
}
|
||
|
||
if (blf_file_seek_end(ctx->fp) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to seek to end of file after container\n");
|
||
return -1;
|
||
}
|
||
|
||
ctx->objectCount++;
|
||
if (ctx->container_timestamp > ctx->maxTimestamp)
|
||
ctx->maxTimestamp = ctx->container_timestamp;
|
||
|
||
ctx->in_container = 0;
|
||
return 0;
|
||
}
|
||
|
||
BLF_API int blf_add_can_message(void* handle,
|
||
uint16_t channel,
|
||
uint32_t id,
|
||
uint8_t flags,
|
||
uint8_t dlc,
|
||
const uint8_t* data,
|
||
uint64_t timestamp) {
|
||
if (!handle) return -1;
|
||
BLFContext* ctx = (BLFContext*)handle;
|
||
if (dlc > 8) {
|
||
fprintf(stderr, "ERROR: CAN message DLC > 8\n");
|
||
return -1;
|
||
}
|
||
|
||
VBLObjectHeader objHdr;
|
||
VBLCANMessage canMsg;
|
||
|
||
canMsg.mChannel = channel;
|
||
canMsg.mFlags = flags;
|
||
canMsg.mDLC = dlc;
|
||
canMsg.mID = id;
|
||
if (data && dlc <= 8) {
|
||
memcpy(canMsg.mData, data, dlc);
|
||
if (dlc < 8) memset(canMsg.mData + dlc, 0, 8 - dlc);
|
||
} else {
|
||
memset(canMsg.mData, 0, 8);
|
||
}
|
||
|
||
objHdr.mBase.mSignature = BL_OBJ_SIGNATURE;
|
||
objHdr.mBase.mHeaderSize = sizeof(VBLObjectHeader);
|
||
objHdr.mBase.mHeaderVersion = 1;
|
||
objHdr.mBase.mObjectSize = sizeof(VBLObjectHeader) + sizeof(VBLCANMessage);
|
||
objHdr.mBase.mObjectType = BL_OBJ_TYPE_CAN_MESSAGE;
|
||
objHdr.mObjectFlags = BL_OBJ_FLAG_TIME_ONE_NANS;
|
||
objHdr.mClientIndex = 0;
|
||
objHdr.mObjectVersion = 0;
|
||
objHdr.mObjectTimeStamp = timestamp;
|
||
|
||
if (blf_file_write(ctx->fp, &objHdr, sizeof(objHdr)) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to write CAN message header\n");
|
||
return -1;
|
||
}
|
||
if (blf_file_write(ctx->fp, &canMsg, sizeof(canMsg)) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to write CAN message body\n");
|
||
return -1;
|
||
}
|
||
|
||
if (!ctx->in_container) ctx->objectCount++;
|
||
if (timestamp > ctx->maxTimestamp) ctx->maxTimestamp = timestamp;
|
||
return 0;
|
||
}
|
||
|
||
BLF_API int blf_add_lin_message_obsolete(void* handle,
|
||
uint16_t channel,
|
||
uint8_t id,
|
||
uint8_t dlc,
|
||
const uint8_t* data,
|
||
uint8_t dir,
|
||
uint64_t timestamp,
|
||
uint16_t checksum) {
|
||
if (!handle) return -1;
|
||
BLFContext* ctx = (BLFContext*)handle;
|
||
if (dlc > 8) {
|
||
fprintf(stderr, "ERROR: LIN message DLC > 8\n");
|
||
return -1;
|
||
}
|
||
|
||
VBLObjectHeader objHdr;
|
||
VBLLINMessage linMsg;
|
||
memset(&linMsg, 0, sizeof(linMsg));
|
||
|
||
linMsg.mChannel = channel;
|
||
linMsg.mID = id & 0x3F;
|
||
linMsg.mDLC = dlc;
|
||
if (data && dlc > 0) memcpy(linMsg.mData, data, dlc);
|
||
|
||
uint8_t header_bits = 34;
|
||
uint8_t response_bits = (dlc + 1) * 10;
|
||
linMsg.mHeaderTime = header_bits;
|
||
linMsg.mFullTime = header_bits + response_bits;
|
||
|
||
linMsg.mCRC = checksum;
|
||
linMsg.mDir = dir;
|
||
linMsg.mReserved = 0;
|
||
|
||
objHdr.mBase.mSignature = BL_OBJ_SIGNATURE;
|
||
objHdr.mBase.mHeaderSize = sizeof(VBLObjectHeader);
|
||
objHdr.mBase.mHeaderVersion = 1;
|
||
objHdr.mBase.mObjectSize = sizeof(VBLObjectHeader) + sizeof(VBLLINMessage);
|
||
objHdr.mBase.mObjectType = BL_OBJ_TYPE_LIN_MESSAGE;
|
||
objHdr.mObjectFlags = BL_OBJ_FLAG_TIME_ONE_NANS;
|
||
objHdr.mClientIndex = 0;
|
||
objHdr.mObjectVersion = 0;
|
||
objHdr.mObjectTimeStamp = timestamp;
|
||
|
||
if (blf_file_write(ctx->fp, &objHdr, sizeof(objHdr)) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to write LIN message header\n");
|
||
return -1;
|
||
}
|
||
if (blf_file_write(ctx->fp, &linMsg, sizeof(linMsg)) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to write LIN message body\n");
|
||
return -1;
|
||
}
|
||
|
||
if (!ctx->in_container) ctx->objectCount++;
|
||
if (timestamp > ctx->maxTimestamp) ctx->maxTimestamp = timestamp;
|
||
return 0;
|
||
}
|
||
|
||
BLF_API int blf_add_lin_send_error(void* handle,
|
||
uint16_t channel,
|
||
uint8_t id,
|
||
uint8_t dlc,
|
||
uint64_t timestamp) {
|
||
if (!handle) return -1;
|
||
BLFContext* ctx = (BLFContext*)handle;
|
||
|
||
VBLObjectHeader objHdr;
|
||
VBLLINSendError sendErr;
|
||
|
||
memset(&sendErr, 0, sizeof(sendErr));
|
||
sendErr.mChannel = channel;
|
||
sendErr.mID = id & 0x3F;
|
||
sendErr.mDLC = dlc;
|
||
sendErr.mHeaderTime = 34;
|
||
sendErr.mFullTime = 34;
|
||
|
||
objHdr.mBase.mSignature = BL_OBJ_SIGNATURE;
|
||
objHdr.mBase.mHeaderSize = sizeof(VBLObjectHeader);
|
||
objHdr.mBase.mHeaderVersion = 1;
|
||
objHdr.mBase.mObjectSize = sizeof(VBLObjectHeader) + sizeof(VBLLINSendError);
|
||
objHdr.mBase.mObjectType = BL_OBJ_TYPE_LIN_SND_ERROR;
|
||
objHdr.mObjectFlags = BL_OBJ_FLAG_TIME_ONE_NANS;
|
||
objHdr.mClientIndex = 0;
|
||
objHdr.mObjectVersion = 0;
|
||
objHdr.mObjectTimeStamp = timestamp;
|
||
|
||
if (blf_file_write(ctx->fp, &objHdr, sizeof(objHdr)) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to write LIN send error header\n");
|
||
return -1;
|
||
}
|
||
if (blf_file_write(ctx->fp, &sendErr, sizeof(sendErr)) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to write LIN send error body\n");
|
||
return -1;
|
||
}
|
||
|
||
if (!ctx->in_container) ctx->objectCount++;
|
||
if (timestamp > ctx->maxTimestamp) ctx->maxTimestamp = timestamp;
|
||
return 0;
|
||
}
|
||
|
||
BLF_API int blf_add_env_data(void* handle,
|
||
const char* name,
|
||
const uint8_t* data,
|
||
uint32_t data_len,
|
||
uint64_t timestamp) {
|
||
if (!handle) return -1;
|
||
BLFContext* ctx = (BLFContext*)handle;
|
||
if (data == NULL && data_len > 0) {
|
||
fprintf(stderr, "ERROR: blf_add_env_data: data is NULL but data_len > 0\n");
|
||
return -1;
|
||
}
|
||
|
||
uint32_t name_len = (name != NULL) ? (uint32_t)strlen(name) : 0;
|
||
uint32_t object_size = sizeof(VBLObjectHeader) + sizeof(uint32_t)*2 + sizeof(uint64_t) + name_len + data_len;
|
||
|
||
VBLObjectHeader objHdr;
|
||
objHdr.mBase.mSignature = BL_OBJ_SIGNATURE;
|
||
objHdr.mBase.mHeaderSize = sizeof(VBLObjectHeader);
|
||
objHdr.mBase.mHeaderVersion = 1;
|
||
objHdr.mBase.mObjectSize = object_size;
|
||
objHdr.mBase.mObjectType = BL_OBJ_TYPE_ENV_DATA;
|
||
objHdr.mObjectFlags = BL_OBJ_FLAG_TIME_ONE_NANS;
|
||
objHdr.mClientIndex = 0;
|
||
objHdr.mObjectVersion = 0;
|
||
objHdr.mObjectTimeStamp = timestamp;
|
||
|
||
if (blf_file_write(ctx->fp, &objHdr, sizeof(objHdr)) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to write ENV_DATA header\n");
|
||
return -1;
|
||
}
|
||
|
||
uint32_t nameLength = name_len;
|
||
uint32_t dataLength = data_len;
|
||
uint64_t reserved = 0;
|
||
if (blf_file_write(ctx->fp, &nameLength, sizeof(nameLength)) != 0 ||
|
||
blf_file_write(ctx->fp, &dataLength, sizeof(dataLength)) != 0 ||
|
||
blf_file_write(ctx->fp, &reserved, sizeof(reserved)) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to write ENV_DATA fields\n");
|
||
return -1;
|
||
}
|
||
|
||
if (name_len > 0 && name != NULL) {
|
||
if (blf_file_write(ctx->fp, name, name_len) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to write ENV_DATA name\n");
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
if (data_len > 0 && data != NULL) {
|
||
if (blf_file_write(ctx->fp, data, data_len) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to write ENV_DATA data\n");
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
/* Выравнивание до 4 байт */
|
||
long pos = blf_file_tell(ctx->fp);
|
||
if (pos < 0) return -1;
|
||
long pad = (4 - (pos % 4)) % 4;
|
||
for (long i = 0; i < pad; i++) {
|
||
uint8_t zero = 0;
|
||
if (blf_file_write(ctx->fp, &zero, 1) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to write ENV_DATA padding\n");
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
if (!ctx->in_container) ctx->objectCount++;
|
||
if (timestamp > ctx->maxTimestamp) ctx->maxTimestamp = timestamp;
|
||
return 0;
|
||
}
|
||
|
||
BLF_API int blf_close(void* handle) {
|
||
if (!handle) return -1;
|
||
BLFContext* ctx = (BLFContext*)handle;
|
||
|
||
/* Закрываем незавершённый контейнер */
|
||
if (ctx->in_container) {
|
||
if (blf_end_container(ctx) != 0) {
|
||
fprintf(stderr, "WARNING: Failed to close remaining container\n");
|
||
}
|
||
}
|
||
|
||
/* Выравнивание файла до 4 байт */
|
||
long endPos = blf_file_tell(ctx->fp);
|
||
if (endPos < 0) {
|
||
fclose(ctx->fp);
|
||
free(ctx);
|
||
return -1;
|
||
}
|
||
long padding = (4 - (endPos % 4)) % 4;
|
||
for (long i = 0; i < padding; i++) {
|
||
uint8_t zero = 0;
|
||
if (blf_file_write(ctx->fp, &zero, 1) != 0) {
|
||
fclose(ctx->fp);
|
||
free(ctx);
|
||
return -1;
|
||
}
|
||
}
|
||
endPos += padding;
|
||
|
||
/* Вычисляем время последнего объекта */
|
||
SYSTEMTIME_PACKED lastObj = ctx->header.measurementStartTime;
|
||
uint64_t total_ms = ctx->maxTimestamp / 1000000;
|
||
systemtime_add_ms(&lastObj, total_ms);
|
||
ctx->header.lastObjectTime = lastObj;
|
||
|
||
/* Обновляем поля заголовка */
|
||
ctx->header.fileSize = (uint64_t)endPos;
|
||
ctx->header.uncompressedFileSize = (uint64_t)endPos;
|
||
ctx->header.objectCount = ctx->objectCount;
|
||
|
||
/* Перезаписываем заголовок */
|
||
if (blf_file_seek(ctx->fp, ctx->headerPos) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to seek to file header\n");
|
||
fclose(ctx->fp);
|
||
free(ctx);
|
||
return -1;
|
||
}
|
||
if (blf_file_write(ctx->fp, &ctx->header, sizeof(FileHeader)) != 0) {
|
||
fprintf(stderr, "ERROR: Failed to rewrite file header\n");
|
||
fclose(ctx->fp);
|
||
free(ctx);
|
||
return -1;
|
||
}
|
||
|
||
fclose(ctx->fp);
|
||
free(ctx);
|
||
return 0;
|
||
} |