// ============================================================================ // block_serializer.c - JSON 序列化实现 // ============================================================================ #include "block_serializer.h" #include "block_types.h" #include #include #include // 简单的 JSON 生成器 typedef struct { char* buffer; size_t size; size_t capacity; } JsonBuffer; static void json_init(JsonBuffer* buf) { buf->capacity = 4096; buf->buffer = (char*)malloc(buf->capacity); buf->size = 0; buf->buffer[0] = '\0'; } static void json_append(JsonBuffer* buf, const char* str) { size_t len = strlen(str); if (buf->size + len + 1 > buf->capacity) { buf->capacity = (buf->size + len + 1) * 2; buf->buffer = (char*)realloc(buf->buffer, buf->capacity); } memcpy(buf->buffer + buf->size, str, len + 1); buf->size += len; } int serializer_save(BlockWorkspace* ws, const char* filepath) { char* json = serializer_save_to_string(ws); if (!json) return 0; FILE* f = fopen(filepath, "w"); if (!f) { free(json); return 0; } fprintf(f, "%s", json); fclose(f); free(json); return 1; } int serializer_load(BlockWorkspace* ws, const char* filepath) { FILE* f = fopen(filepath, "r"); if (!f) return 0; fseek(f, 0, SEEK_END); long size = ftell(f); fseek(f, 0, SEEK_SET); char* json = (char*)malloc(size + 1); fread(json, 1, size, f); json[size] = '\0'; fclose(f); int result = serializer_load_from_string(ws, json); free(json); return result; } char* serializer_save_to_string(BlockWorkspace* ws) { JsonBuffer buf; json_init(&buf); json_append(&buf, "{\n"); json_append(&buf, " \"version\": \"1.0\",\n"); // 保存积木 json_append(&buf, " \"blocks\": [\n"); for (int i = 0; i < ws->block_count; i++) { Block* block = ws->blocks[i]; const BlockTypeInfo* info = block_type_get_info(block->type); char tmp[256]; snprintf(tmp, 256, " {\n"); json_append(&buf, tmp); snprintf(tmp, 256, " \"id\": %d,\n", block->id); json_append(&buf, tmp); snprintf(tmp, 256, " \"type\": \"%s\",\n", info ? info->name : "unknown"); json_append(&buf, tmp); snprintf(tmp, 256, " \"x\": %.1f,\n", block->x); json_append(&buf, tmp); snprintf(tmp, 256, " \"y\": %.1f,\n", block->y); json_append(&buf, tmp); snprintf(tmp, 256, " \"label\": \"%s\"", block->label); json_append(&buf, tmp); // 保存输入槽默认值 if (block->input_count > 0) { json_append(&buf, ",\n \"inputs\": [\n"); for (int j = 0; j < block->input_count; j++) { BlockSlot* slot = &block->inputs[j]; SlotValue* val = &slot->value; switch (val->type) { case SLOT_VALUE_NUMBER: snprintf(tmp, 256, " {\"type\": \"number\", \"value\": %.6g}%s\n", val->as.number, j < block->input_count - 1 ? "," : ""); break; case SLOT_VALUE_STRING: snprintf(tmp, 256, " {\"type\": \"string\", \"value\": \"%s\"}%s\n", val->as.string, j < block->input_count - 1 ? "," : ""); break; case SLOT_VALUE_BOOLEAN: snprintf(tmp, 256, " {\"type\": \"boolean\", \"value\": %s}%s\n", val->as.boolean ? "true" : "false", j < block->input_count - 1 ? "," : ""); break; default: snprintf(tmp, 256, " {\"type\": \"number\", \"value\": 0}%s\n", j < block->input_count - 1 ? "," : ""); break; } json_append(&buf, tmp); } json_append(&buf, " ]"); } json_append(&buf, "\n"); json_append(&buf, i < ws->block_count - 1 ? " },\n" : " }\n"); } json_append(&buf, " ],\n"); // 保存连接 json_append(&buf, " \"connections\": [\n"); for (int i = 0; i < ws->connection_count; i++) { BlockConnection* conn = &ws->connections[i]; char tmp[128]; snprintf(tmp, 128, " {\"from\": %d, \"to\": %d}%s\n", conn->from ? conn->from->owner->id : 0, conn->to ? conn->to->owner->id : 0, i < ws->connection_count - 1 ? "," : ""); json_append(&buf, tmp); } json_append(&buf, " ]\n"); json_append(&buf, "}\n"); return buf.buffer; } // 跳过空白字符 static const char* skip_ws(const char* p) { while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++; return p; } // 查找字符串值,返回指向引号内内容的指针,并在 *len 中设置长度 static const char* parse_json_string(const char* p, int* len) { p = skip_ws(p); if (*p != '"') return NULL; p++; const char* start = p; while (*p && *p != '"') p++; if (*p != '"') return NULL; *len = (int)(p - start); return start; } // 查找数字值,返回指向数字开头的指针 static const char* parse_json_number(const char* p, float* out) { p = skip_ws(p); const char* start = p; if (*p == '-') p++; while (*p >= '0' && *p <= '9') p++; if (*p == '.') { p++; while (*p >= '0' && *p <= '9') p++; } if (p == start) return NULL; *out = (float)strtod(start, NULL); return p; } int serializer_load_from_string(BlockWorkspace* ws, const char* json_str) { workspace_clear(ws); block_types_init(); // 解析 blocks 数组 const char* blocks_key = strstr(json_str, "\"blocks\""); if (!blocks_key) return 0; const char* p = blocks_key + 8; p = skip_ws(p); if (*p != ':') return 0; p++; p = skip_ws(p); if (*p != '[') return 0; p++; while (1) { p = skip_ws(p); if (*p == ']') { p++; break; } if (*p != '{') return 0; p++; int id = 0; char type_name[64] = {0}; char label[64] = {0}; float x = 0, y = 0; SlotValue input_values[MAX_BLOCK_INPUTS]; int input_count = 0; while (1) { p = skip_ws(p); if (*p == '}') { p++; break; } int key_len = 0; const char* key = parse_json_string(p, &key_len); if (!key) return 0; p = key + key_len + 1; p = skip_ws(p); if (*p != ':') return 0; p++; if (key_len == 2 && strncmp(key, "id", 2) == 0) { float fv; p = parse_json_number(p, &fv); id = (int)fv; } else if (key_len == 4 && strncmp(key, "type", 4) == 0) { int val_len = 0; const char* val = parse_json_string(p, &val_len); if (val) { if (val_len >= (int)sizeof(type_name)) val_len = (int)sizeof(type_name) - 1; strncpy(type_name, val, val_len); type_name[val_len] = '\0'; p = val + val_len + 1; } } else if (key_len == 1 && *key == 'x') { p = parse_json_number(p, &x); } else if (key_len == 1 && *key == 'y') { p = parse_json_number(p, &y); } else if (key_len == 5 && strncmp(key, "label", 5) == 0) { int val_len = 0; const char* val = parse_json_string(p, &val_len); if (val) { if (val_len >= (int)sizeof(label)) val_len = (int)sizeof(label) - 1; strncpy(label, val, val_len); label[val_len] = '\0'; p = val + val_len + 1; } } else if (key_len == 6 && strncmp(key, "inputs", 6) == 0) { p = skip_ws(p); if (*p == '[') { p++; input_count = 0; while (1) { p = skip_ws(p); if (*p == ']') { p++; break; } if (*p != '{') break; p++; char iv_type[16] = {0}; float fv = 0; int bv = 0; char sval_str[256] = {0}; while (1) { p = skip_ws(p); if (*p == '}') { p++; break; } int ik_len = 0; const char* ik = parse_json_string(p, &ik_len); if (!ik) break; p = ik + ik_len + 1; p = skip_ws(p); if (*p != ':') break; p++; if (ik_len == 4 && strncmp(ik, "type", 4) == 0) { int iv_len = 0; const char* iv = parse_json_string(p, &iv_len); if (iv) { if (iv_len >= (int)sizeof(iv_type)) iv_len = (int)sizeof(iv_type) - 1; strncpy(iv_type, iv, iv_len); iv_type[iv_len] = '\0'; p = iv + iv_len + 1; } } else if (ik_len == 5 && strncmp(ik, "value", 5) == 0) { p = skip_ws(p); if (*p == '"') { int sv_len = 0; const char* sval = parse_json_string(p, &sv_len); if (sval) { if (sv_len >= (int)sizeof(sval_str)) sv_len = (int)sizeof(sval_str) - 1; strncpy(sval_str, sval, sv_len); sval_str[sv_len] = '\0'; p = sval + sv_len + 1; } } else if (strncmp(p, "true", 4) == 0) { bv = 1; p += 4; } else if (strncmp(p, "false", 5) == 0) { bv = 0; p += 5; } else { p = parse_json_number(p, &fv); } } else { while (*p && *p != ',' && *p != '}') p++; } p = skip_ws(p); if (*p == ',') p++; } // 暂存输入值 if (input_count < MAX_BLOCK_INPUTS) { SlotValue* sv = &input_values[input_count++]; memset(sv, 0, sizeof(SlotValue)); if (strcmp(iv_type, "number") == 0) { sv->type = SLOT_VALUE_NUMBER; sv->as.number = (double)fv; } else if (strcmp(iv_type, "string") == 0) { sv->type = SLOT_VALUE_STRING; strncpy(sv->as.string, sval_str, sizeof(sv->as.string) - 1); } else if (strcmp(iv_type, "boolean") == 0) { sv->type = SLOT_VALUE_BOOLEAN; sv->as.boolean = bv; } else { sv->type = SLOT_VALUE_NUMBER; sv->as.number = 0; } } p = skip_ws(p); if (*p == ',') p++; } } } else { // 跳过未知值 (简单跳过直到逗号或右花括号) while (*p && *p != ',' && *p != '}') p++; } p = skip_ws(p); if (*p == ',') p++; } BlockType type = block_type_find_by_name(type_name); if (type == BLOCK_TYPE_COUNT || type_name[0] == '\0') { type = BLOCK_EVENT_START; // fallback } Block* block = block_create(type, x, y); if (block) { block->id = id; strncpy(block->label, label, sizeof(block->label) - 1); block->label[sizeof(block->label) - 1] = '\0'; for (int j = 0; j < input_count && j < block->input_count; j++) { block->inputs[j].value = input_values[j]; } workspace_add_block(ws, block); if (id >= ws->next_block_id) ws->next_block_id = id + 1; } p = skip_ws(p); if (*p == ',') p++; } // 解析 connections 数组 const char* conn_key = strstr(json_str, "\"connections\""); if (conn_key) { p = conn_key + 13; p = skip_ws(p); if (*p == ':') { p++; p = skip_ws(p); if (*p == '[') { p++; while (1) { p = skip_ws(p); if (*p == ']') { p++; break; } if (*p != '{') break; p++; int from_id = -1, to_id = -1; while (1) { p = skip_ws(p); if (*p == '}') { p++; break; } int key_len = 0; const char* key = parse_json_string(p, &key_len); if (!key) break; p = key + key_len + 1; p = skip_ws(p); if (*p != ':') break; p++; float fv = 0; if (key_len == 4 && strncmp(key, "from", 4) == 0) { p = parse_json_number(p, &fv); from_id = (int)fv; } else if (key_len == 2 && strncmp(key, "to", 2) == 0) { p = parse_json_number(p, &fv); to_id = (int)fv; } else { while (*p && *p != ',' && *p != '}') p++; } p = skip_ws(p); if (*p == ',') p++; } if (from_id >= 0 && to_id >= 0) { Block* from_block = workspace_find_block(ws, from_id); Block* to_block = workspace_find_block(ws, to_id); if (from_block && to_block && from_block->output_count > 0 && to_block->input_count > 0) { workspace_add_connection(ws, &from_block->outputs[0], &to_block->inputs[0]); } } p = skip_ws(p); if (*p == ',') p++; } } } } return 1; }