639 lines
19 KiB
C
639 lines
19 KiB
C
#include "emoji_storage.h"
|
||||
|
|
#include "FLASH/flash.h"
|
|||
|
|
#include "nvs_flash.h"
|
|||
|
|
#include "nvs.h"
|
|||
|
|
#include "esp_log.h"
|
|||
|
|
#include <string.h>
|
|||
|
|
#include <stdlib.h>
|
|||
|
|
#include <dirent.h>
|
|||
|
|
#include <sys/stat.h>
|
|||
|
|
#include <unistd.h>
|
|||
|
|
#include "cjson.h"
|
|||
|
|
|
|||
|
|
static const char *TAG = "emoji_storage";
|
|||
|
|
|
|||
|
|
// NVS命名空间
|
|||
|
|
#define NVS_NAMESPACE "emoji_config"//"emoji_config"
|
|||
|
|
#define USING_PACK_KEY "using_pack"
|
|||
|
|
/* NVS key 最大 15 字符,原 "downloaded_packs" 超长导致 ESP_ERR_NVS_KEY_TOO_LONG */
|
|||
|
|
#define DOWNLOADED_PACKS_KEY "dl_packs"
|
|||
|
|
|
|||
|
|
#define EXTFLASH_BASE "/extflash"
|
|||
|
|
|
|||
|
|
// 内存缓存
|
|||
|
|
static char g_using_pack = '0'; // 默认表情包
|
|||
|
|
static char g_downloaded_packs[256] = {0}; // 存储JSON格式的已下载列表
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 根据 /extflash 下实际存在的 A/B/C/D 目录同步已下载列表并写回 NVS
|
|||
|
|
* 解决“昨天下载了 A 但当时 NVS key 超长保存失败、查询只返回 0”的问题
|
|||
|
|
*/
|
|||
|
|
static void emoji_storage_sync_downloaded_packs_from_fs(void)
|
|||
|
|
{
|
|||
|
|
extflash_lock();
|
|||
|
|
DIR *dir = opendir(EXTFLASH_BASE);
|
|||
|
|
if (!dir) {
|
|||
|
|
extflash_unlock();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
cJSON *json_array = cJSON_Parse(g_downloaded_packs);
|
|||
|
|
if (!json_array || !cJSON_IsArray(json_array)) {
|
|||
|
|
if (json_array) {
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
}
|
|||
|
|
json_array = cJSON_CreateArray();
|
|||
|
|
if (!json_array) {
|
|||
|
|
closedir(dir);
|
|||
|
|
extflash_unlock();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
cJSON_AddItemToArray(json_array, cJSON_CreateString("0"));
|
|||
|
|
}
|
|||
|
|
bool changed = false;
|
|||
|
|
struct dirent *entry;
|
|||
|
|
while ((entry = readdir(dir)) != NULL) {
|
|||
|
|
if (entry->d_name[0] == '.') {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
if (entry->d_name[1] != '\0') {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
char pack_id = entry->d_name[0];
|
|||
|
|
if (pack_id != 'A' && pack_id != 'B' && pack_id != 'C' && pack_id != 'D') {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
char path[32];
|
|||
|
|
snprintf(path, sizeof(path), "%s/%c", EXTFLASH_BASE, pack_id);
|
|||
|
|
struct stat st;
|
|||
|
|
if (stat(path, &st) != 0 || !S_ISDIR(st.st_mode)) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
bool found = false;
|
|||
|
|
int n = cJSON_GetArraySize(json_array);
|
|||
|
|
for (int i = 0; i < n; i++) {
|
|||
|
|
cJSON *item = cJSON_GetArrayItem(json_array, i);
|
|||
|
|
if (item && cJSON_IsString(item) && item->valuestring[0] == pack_id) {
|
|||
|
|
found = true;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (!found) {
|
|||
|
|
char pack_str[2] = {pack_id, '\0'};
|
|||
|
|
cJSON_AddItemToArray(json_array, cJSON_CreateString(pack_str));
|
|||
|
|
changed = true;
|
|||
|
|
ESP_LOGI(TAG, "从 /extflash 同步到已下载列表: %c", pack_id);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
closedir(dir);
|
|||
|
|
extflash_unlock();
|
|||
|
|
/* 已安装列表始终包含默认表情包系列 0(内置) */
|
|||
|
|
{
|
|||
|
|
bool has_zero = false;
|
|||
|
|
int n = cJSON_GetArraySize(json_array);
|
|||
|
|
for (int i = 0; i < n; i++) {
|
|||
|
|
cJSON *item = cJSON_GetArrayItem(json_array, i);
|
|||
|
|
if (item && cJSON_IsString(item) && item->valuestring[0] == '0') {
|
|||
|
|
has_zero = true;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (!has_zero) {
|
|||
|
|
cJSON_AddItemToArray(json_array, cJSON_CreateString("0"));
|
|||
|
|
changed = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (!changed) {
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
char *json_string = cJSON_PrintUnformatted(json_array);
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
if (!json_string) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
strncpy(g_downloaded_packs, json_string, sizeof(g_downloaded_packs) - 1);
|
|||
|
|
g_downloaded_packs[sizeof(g_downloaded_packs) - 1] = '\0';
|
|||
|
|
free(json_string);
|
|||
|
|
|
|||
|
|
nvs_handle_t nvs_handle;
|
|||
|
|
if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle) != ESP_OK) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (nvs_set_str(nvs_handle, DOWNLOADED_PACKS_KEY, g_downloaded_packs) == ESP_OK) {
|
|||
|
|
nvs_commit(nvs_handle);
|
|||
|
|
ESP_LOGI(TAG, "已下载列表已同步到 NVS: %s", g_downloaded_packs);
|
|||
|
|
}
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 从NVS加载表情包信息到内存
|
|||
|
|
*/
|
|||
|
|
esp_err_t emoji_storage_load(void)
|
|||
|
|
{
|
|||
|
|
nvs_handle_t nvs_handle;
|
|||
|
|
esp_err_t err;
|
|||
|
|
size_t required_size;
|
|||
|
|
|
|||
|
|
// 打开NVS命名空间
|
|||
|
|
err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs_handle);
|
|||
|
|
if (err != ESP_OK) {
|
|||
|
|
ESP_LOGW(TAG, "打开NVS命名空间失败: %s,使用默认值", esp_err_to_name(err));
|
|||
|
|
g_using_pack = '0';
|
|||
|
|
strcpy(g_downloaded_packs, "[\"0\"]"); // 默认只有'0'
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 读取正在使用的表情包
|
|||
|
|
char using_pack_str[2] = {0};
|
|||
|
|
required_size = sizeof(using_pack_str);
|
|||
|
|
err = nvs_get_str(nvs_handle, USING_PACK_KEY, using_pack_str, &required_size);
|
|||
|
|
if (err == ESP_OK && required_size > 0) {
|
|||
|
|
g_using_pack = using_pack_str[0];
|
|||
|
|
ESP_LOGI(TAG, "加载正在使用的表情包: %c", g_using_pack);
|
|||
|
|
} else {
|
|||
|
|
ESP_LOGW(TAG, "读取using_pack失败: %s,使用默认值'0'", esp_err_to_name(err));
|
|||
|
|
g_using_pack = '0';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 读取已下载的表情包列表
|
|||
|
|
required_size = sizeof(g_downloaded_packs);
|
|||
|
|
err = nvs_get_str(nvs_handle, DOWNLOADED_PACKS_KEY, g_downloaded_packs, &required_size);
|
|||
|
|
if (err == ESP_OK && required_size > 0) {
|
|||
|
|
ESP_LOGI(TAG, "加载已下载表情包列表: %s", g_downloaded_packs);
|
|||
|
|
} else {
|
|||
|
|
strcpy(g_downloaded_packs, "[\"0\"]");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
|
|||
|
|
/* 根据 /extflash 下实际存在的 A/B/C/D 目录同步已下载列表,避免“昨天下了 A 但 NVS 未保存”只返回 0 */
|
|||
|
|
emoji_storage_sync_downloaded_packs_from_fs();
|
|||
|
|
|
|||
|
|
return ESP_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 保存当前使用的表情包ID到NVS
|
|||
|
|
*/
|
|||
|
|
esp_err_t emoji_storage_save_using_pack(char pack_id)
|
|||
|
|
{
|
|||
|
|
ESP_LOGI(TAG, "[EMOJI_DEBUG] emoji_storage_save_using_pack('%c') 被调用, 当前 g_using_pack='%c'", pack_id, g_using_pack);
|
|||
|
|
|
|||
|
|
nvs_handle_t nvs_handle;
|
|||
|
|
esp_err_t err;
|
|||
|
|
char pack_str[2] = {pack_id, '\0'};
|
|||
|
|
|
|||
|
|
err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
|
|||
|
|
if (err != ESP_OK) {
|
|||
|
|
ESP_LOGE(TAG, "[EMOJI_DEBUG] 打开NVS命名空间失败: %s", esp_err_to_name(err));
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
err = nvs_set_str(nvs_handle, USING_PACK_KEY, pack_str);
|
|||
|
|
if (err != ESP_OK) {
|
|||
|
|
ESP_LOGE(TAG, "[EMOJI_DEBUG] 保存 using_pack 到 NVS 失败: %s", esp_err_to_name(err));
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
err = nvs_commit(nvs_handle);
|
|||
|
|
if (err != ESP_OK) {
|
|||
|
|
ESP_LOGE(TAG, "[EMOJI_DEBUG] 提交NVS更改失败: %s", esp_err_to_name(err));
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
g_using_pack = pack_id;
|
|||
|
|
ESP_LOGI(TAG, "[EMOJI_DEBUG] 成功保存 using_pack='%c' 到 NVS, g_using_pack 已更新", pack_id);
|
|||
|
|
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
return ESP_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 添加已下载的表情包ID到列表并保存到NVS
|
|||
|
|
*/
|
|||
|
|
esp_err_t emoji_storage_add_downloaded_pack(char pack_id)
|
|||
|
|
{
|
|||
|
|
ESP_LOGI(TAG, "[EMOJI_DEBUG] emoji_storage_add_downloaded_pack('%c') 被调用", pack_id);
|
|||
|
|
ESP_LOGI(TAG, "[EMOJI_DEBUG] 当前 g_using_pack='%c', g_downloaded_packs=%s", g_using_pack, g_downloaded_packs);
|
|||
|
|
|
|||
|
|
nvs_handle_t nvs_handle;
|
|||
|
|
esp_err_t err;
|
|||
|
|
cJSON *json_array = NULL;
|
|||
|
|
cJSON *item = NULL;
|
|||
|
|
char pack_str[2] = {pack_id, '\0'};
|
|||
|
|
bool found = false;
|
|||
|
|
|
|||
|
|
if (emoji_storage_is_downloaded(pack_id)) {
|
|||
|
|
ESP_LOGW(TAG, "[EMOJI_DEBUG] 表情包 %c 已存在于下载列表中, 直接返回 ESP_OK", pack_id);
|
|||
|
|
return ESP_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 打开NVS命名空间
|
|||
|
|
err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
|
|||
|
|
if (err != ESP_OK) {
|
|||
|
|
ESP_LOGE(TAG, "打开NVS命名空间失败: %s", esp_err_to_name(err));
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 读取现有的JSON数组
|
|||
|
|
size_t required_size = sizeof(g_downloaded_packs);
|
|||
|
|
err = nvs_get_str(nvs_handle, DOWNLOADED_PACKS_KEY, g_downloaded_packs, &required_size);
|
|||
|
|
if (err != ESP_OK) {
|
|||
|
|
// 如果不存在,创建新数组
|
|||
|
|
json_array = cJSON_CreateArray();
|
|||
|
|
} else {
|
|||
|
|
// 解析现有JSON
|
|||
|
|
json_array = cJSON_Parse(g_downloaded_packs);
|
|||
|
|
if (json_array == NULL || !cJSON_IsArray(json_array)) {
|
|||
|
|
// 解析失败,创建新数组
|
|||
|
|
if (json_array) {
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
}
|
|||
|
|
json_array = cJSON_CreateArray();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否已存在
|
|||
|
|
int array_size = cJSON_GetArraySize(json_array);
|
|||
|
|
for (int i = 0; i < array_size; i++) {
|
|||
|
|
cJSON *existing_item = cJSON_GetArrayItem(json_array, i);
|
|||
|
|
if (existing_item && cJSON_IsString(existing_item)) {
|
|||
|
|
if (existing_item->valuestring[0] == pack_id) {
|
|||
|
|
found = true;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果不存在,添加到数组
|
|||
|
|
if (!found) {
|
|||
|
|
item = cJSON_CreateString(pack_str);
|
|||
|
|
cJSON_AddItemToArray(json_array, item);
|
|||
|
|
ESP_LOGI(TAG, "添加表情包 %c 到下载列表", pack_id);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 转换为JSON字符串
|
|||
|
|
char *json_string = cJSON_PrintUnformatted(json_array);
|
|||
|
|
if (json_string == NULL) {
|
|||
|
|
ESP_LOGE(TAG, "JSON序列化失败");
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
return ESP_ERR_NO_MEM;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 保存到NVS
|
|||
|
|
err = nvs_set_str(nvs_handle, DOWNLOADED_PACKS_KEY, json_string);
|
|||
|
|
if (err != ESP_OK) {
|
|||
|
|
ESP_LOGE(TAG, "保存downloaded_packs失败: %s", esp_err_to_name(err));
|
|||
|
|
free(json_string);
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 提交更改
|
|||
|
|
err = nvs_commit(nvs_handle);
|
|||
|
|
if (err != ESP_OK) {
|
|||
|
|
ESP_LOGE(TAG, "提交NVS更改失败: %s", esp_err_to_name(err));
|
|||
|
|
free(json_string);
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新内存缓存
|
|||
|
|
strncpy(g_downloaded_packs, json_string, sizeof(g_downloaded_packs) - 1);
|
|||
|
|
g_downloaded_packs[sizeof(g_downloaded_packs) - 1] = '\0';
|
|||
|
|
|
|||
|
|
free(json_string);
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
return ESP_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 获取当前使用的表情包ID
|
|||
|
|
*/
|
|||
|
|
char emoji_storage_get_using_pack(void)
|
|||
|
|
{
|
|||
|
|
return g_using_pack;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 获取已下载的表情包列表(JSON格式字符串)
|
|||
|
|
*/
|
|||
|
|
int emoji_storage_get_downloaded_packs_json(char *packs, int max_len)
|
|||
|
|
{
|
|||
|
|
if (packs == NULL || max_len <= 0) {
|
|||
|
|
return -1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int len = strlen(g_downloaded_packs);
|
|||
|
|
if (len >= max_len) {
|
|||
|
|
ESP_LOGW(TAG, "缓冲区太小,需要 %d 字节,但只有 %d 字节", len + 1, max_len);
|
|||
|
|
return -1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
strncpy(packs, g_downloaded_packs, max_len - 1);
|
|||
|
|
packs[max_len - 1] = '\0';
|
|||
|
|
return len;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 获取已下载的表情包列表(逗号分隔字符串)
|
|||
|
|
*/
|
|||
|
|
int emoji_storage_get_downloaded_packs(char *packs, int max_len)
|
|||
|
|
{
|
|||
|
|
if (packs == NULL || max_len <= 0) {
|
|||
|
|
return -1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 解析JSON数组
|
|||
|
|
cJSON *json_array = cJSON_Parse(g_downloaded_packs);
|
|||
|
|
if (json_array == NULL || !cJSON_IsArray(json_array)) {
|
|||
|
|
ESP_LOGE(TAG, "解析JSON失败");
|
|||
|
|
if (json_array) {
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
}
|
|||
|
|
return -1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int array_size = cJSON_GetArraySize(json_array);
|
|||
|
|
int pos = 0;
|
|||
|
|
|
|||
|
|
for (int i = 0; i < array_size; i++) {
|
|||
|
|
cJSON *item = cJSON_GetArrayItem(json_array, i);
|
|||
|
|
if (item && cJSON_IsString(item) && item->valuestring[0] != '\0') {
|
|||
|
|
if (pos > 0 && pos < max_len - 1) {
|
|||
|
|
packs[pos++] = ',';
|
|||
|
|
}
|
|||
|
|
if (pos < max_len - 1) {
|
|||
|
|
packs[pos++] = item->valuestring[0];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
packs[pos] = '\0';
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
return pos;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 检查指定表情包是否已下载
|
|||
|
|
*/
|
|||
|
|
bool emoji_storage_is_downloaded(char pack_id)
|
|||
|
|
{
|
|||
|
|
// 解析JSON数组
|
|||
|
|
cJSON *json_array = cJSON_Parse(g_downloaded_packs);
|
|||
|
|
if (json_array == NULL || !cJSON_IsArray(json_array)) {
|
|||
|
|
if (json_array) {
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int array_size = cJSON_GetArraySize(json_array);
|
|||
|
|
for (int i = 0; i < array_size; i++) {
|
|||
|
|
cJSON *item = cJSON_GetArrayItem(json_array, i);
|
|||
|
|
if (item && cJSON_IsString(item)) {
|
|||
|
|
if (item->valuestring[0] == pack_id) {
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 从列表中删除指定的表情包
|
|||
|
|
*/
|
|||
|
|
esp_err_t emoji_storage_remove_downloaded_pack(char pack_id)
|
|||
|
|
{
|
|||
|
|
// 不允许删除默认表情包'0'
|
|||
|
|
if (pack_id == '0') {
|
|||
|
|
ESP_LOGW(TAG, "不允许删除默认表情包'0'");
|
|||
|
|
return ESP_ERR_INVALID_ARG;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
nvs_handle_t nvs_handle;
|
|||
|
|
esp_err_t err;
|
|||
|
|
|
|||
|
|
// 打开NVS命名空间
|
|||
|
|
err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
|
|||
|
|
if (err != ESP_OK) {
|
|||
|
|
ESP_LOGE(TAG, "打开NVS命名空间失败: %s", esp_err_to_name(err));
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 解析JSON数组
|
|||
|
|
cJSON *json_array = cJSON_Parse(g_downloaded_packs);
|
|||
|
|
if (json_array == NULL || !cJSON_IsArray(json_array)) {
|
|||
|
|
ESP_LOGE(TAG, "解析JSON失败");
|
|||
|
|
if (json_array) {
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
}
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
return ESP_ERR_INVALID_STATE;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 查找并删除
|
|||
|
|
int array_size = cJSON_GetArraySize(json_array);
|
|||
|
|
for (int i = 0; i < array_size; i++) {
|
|||
|
|
cJSON *item = cJSON_GetArrayItem(json_array, i);
|
|||
|
|
if (item && cJSON_IsString(item)) {
|
|||
|
|
if (item->valuestring[0] == pack_id) {
|
|||
|
|
cJSON_DeleteItemFromArray(json_array, i);
|
|||
|
|
ESP_LOGI(TAG, "从下载列表删除表情包: %c", pack_id);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 转换为JSON字符串
|
|||
|
|
char *json_string = cJSON_PrintUnformatted(json_array);
|
|||
|
|
if (json_string == NULL) {
|
|||
|
|
ESP_LOGE(TAG, "JSON序列化失败");
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
return ESP_ERR_NO_MEM;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 保存到NVS
|
|||
|
|
err = nvs_set_str(nvs_handle, DOWNLOADED_PACKS_KEY, json_string);
|
|||
|
|
if (err != ESP_OK) {
|
|||
|
|
ESP_LOGE(TAG, "保存downloaded_packs失败: %s", esp_err_to_name(err));
|
|||
|
|
free(json_string);
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 提交更改
|
|||
|
|
err = nvs_commit(nvs_handle);
|
|||
|
|
if (err != ESP_OK) {
|
|||
|
|
ESP_LOGE(TAG, "提交NVS更改失败: %s", esp_err_to_name(err));
|
|||
|
|
free(json_string);
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新内存缓存
|
|||
|
|
strncpy(g_downloaded_packs, json_string, sizeof(g_downloaded_packs) - 1);
|
|||
|
|
g_downloaded_packs[sizeof(g_downloaded_packs) - 1] = '\0';
|
|||
|
|
|
|||
|
|
free(json_string);
|
|||
|
|
cJSON_Delete(json_array);
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
|
|||
|
|
if (g_using_pack == pack_id)
|
|||
|
|
{
|
|||
|
|
ESP_LOGW(TAG, "被删除的表情包 '%c' 正是当前使用中的,重置为默认 '0'", pack_id);
|
|||
|
|
emoji_storage_save_using_pack('0');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return ESP_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 递归删除指定目录及其内容
|
|||
|
|
*/
|
|||
|
|
static esp_err_t remove_directory_recursive(const char *path)
|
|||
|
|
{
|
|||
|
|
DIR *dir = opendir(path);
|
|||
|
|
if (!dir) {
|
|||
|
|
ESP_LOGW(TAG, "无法打开目录: %s", path);
|
|||
|
|
return ESP_ERR_NOT_FOUND;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
struct dirent *entry;
|
|||
|
|
char filepath[512];
|
|||
|
|
while ((entry = readdir(dir)) != NULL) {
|
|||
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
snprintf(filepath, sizeof(filepath), "%s/%s", path, entry->d_name);
|
|||
|
|
struct stat st;
|
|||
|
|
if (stat(filepath, &st) != 0) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
if (S_ISDIR(st.st_mode)) {
|
|||
|
|
remove_directory_recursive(filepath);
|
|||
|
|
} else {
|
|||
|
|
if (unlink(filepath) != 0) {
|
|||
|
|
ESP_LOGW(TAG, "删除文件失败: %s", filepath);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
closedir(dir);
|
|||
|
|
|
|||
|
|
if (rmdir(path) != 0) {
|
|||
|
|
ESP_LOGW(TAG, "删除目录失败: %s", path);
|
|||
|
|
return ESP_FAIL;
|
|||
|
|
}
|
|||
|
|
ESP_LOGI(TAG, "已删除目录: %s", path);
|
|||
|
|
return ESP_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 删除指定表情包的文件(目录 + zip)
|
|||
|
|
*/
|
|||
|
|
esp_err_t emoji_storage_remove_pack_files(char pack_id)
|
|||
|
|
{
|
|||
|
|
if (pack_id == '0') {
|
|||
|
|
ESP_LOGW(TAG, "不允许删除默认表情包'0'的文件");
|
|||
|
|
return ESP_ERR_INVALID_ARG;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
char dir_path[32];
|
|||
|
|
char zip_path[32];
|
|||
|
|
snprintf(dir_path, sizeof(dir_path), "%s/%c", EXTFLASH_BASE, pack_id);
|
|||
|
|
snprintf(zip_path, sizeof(zip_path), "%s/%c.zip", EXTFLASH_BASE, pack_id);
|
|||
|
|
|
|||
|
|
struct stat st;
|
|||
|
|
if (stat(dir_path, &st) == 0 && S_ISDIR(st.st_mode)) {
|
|||
|
|
remove_directory_recursive(dir_path);
|
|||
|
|
}
|
|||
|
|
if (stat(zip_path, &st) == 0 && !S_ISDIR(st.st_mode)) {
|
|||
|
|
if (unlink(zip_path) == 0) {
|
|||
|
|
ESP_LOGI(TAG, "已删除zip文件: %s", zip_path);
|
|||
|
|
} else {
|
|||
|
|
ESP_LOGW(TAG, "删除zip文件失败: %s", zip_path);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return ESP_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 清除所有已下载的表情包(删除文件 + 清空NVS + 重置为默认表情包)
|
|||
|
|
*/
|
|||
|
|
esp_err_t emoji_storage_clear_all_packs(void)
|
|||
|
|
{
|
|||
|
|
const char packs[] = {'A', 'B', 'C', 'D'};
|
|||
|
|
for (int i = 0; i < (int)(sizeof(packs) / sizeof(packs[0])); i++) {
|
|||
|
|
emoji_storage_remove_pack_files(packs[i]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
esp_err_t err = emoji_storage_clear_downloaded_packs();
|
|||
|
|
if (err != ESP_OK) {
|
|||
|
|
ESP_LOGE(TAG, "清空NVS已下载列表失败: %s", esp_err_to_name(err));
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (g_using_pack != '0') {
|
|||
|
|
err = emoji_storage_save_using_pack('0');
|
|||
|
|
if (err != ESP_OK) {
|
|||
|
|
ESP_LOGE(TAG, "重置使用中的表情包失败: %s", esp_err_to_name(err));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ESP_LOGI(TAG, "已清除所有已下载的表情包,恢复为默认表情包'0'");
|
|||
|
|
return ESP_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 清空已下载的表情包列表(保留默认表情包'0')
|
|||
|
|
*/
|
|||
|
|
esp_err_t emoji_storage_clear_downloaded_packs(void)
|
|||
|
|
{
|
|||
|
|
nvs_handle_t nvs_handle;
|
|||
|
|
esp_err_t err;
|
|||
|
|
|
|||
|
|
// 打开NVS命名空间
|
|||
|
|
err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
|
|||
|
|
if (err != ESP_OK) {
|
|||
|
|
ESP_LOGE(TAG, "打开NVS命名空间失败: %s", esp_err_to_name(err));
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 只保留默认表情包'0'
|
|||
|
|
const char *default_packs = "[\"0\"]";
|
|||
|
|
err = nvs_set_str(nvs_handle, DOWNLOADED_PACKS_KEY, default_packs);
|
|||
|
|
if (err != ESP_OK) {
|
|||
|
|
ESP_LOGE(TAG, "保存downloaded_packs失败: %s", esp_err_to_name(err));
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 提交更改
|
|||
|
|
err = nvs_commit(nvs_handle);
|
|||
|
|
if (err != ESP_OK) {
|
|||
|
|
ESP_LOGE(TAG, "提交NVS更改失败: %s", esp_err_to_name(err));
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新内存缓存
|
|||
|
|
strcpy(g_downloaded_packs, default_packs);
|
|||
|
|
ESP_LOGI(TAG, "清空已下载表情包列表,保留默认表情包'0'");
|
|||
|
|
|
|||
|
|
nvs_close(nvs_handle);
|
|||
|
|
return ESP_OK;
|
|||
|
|
}
|