#pragma once #include #include #include #include #include #include "rapidxml/rapidxml.hpp" class AudioConfigReader { public: // -------------------------- // 1. 结构体定义:放在类内public区,外部可通过 AudioConfigReader::SoundEffect 访问 // -------------------------- struct SoundEffect { std::string file_path; // 文件路径(FILE属性) int volume_adjust = 100; // 音量调整(默认100) int priority = 0; // 优先级(默认0) }; struct RandomItem { std::string tag; // 关联的音效ID(TAG属性) int prob = 0; // 概率(PROB属性) }; struct RandomSoundGroup { std::vector items; // 组内所有项 int delay = 0; // 延迟(DELAY属性) int loop_delay = 0; // 循环延迟 int loop_delay_range = 0; // 循环延迟范围 }; struct Music { std::string file_path; // 文件路径(FILE属性) int loop_delay = 0; // 循环延迟(LOOP_DELAY属性) }; public: // 构造函数:初始化解析器 AudioConfigReader() = default; // 析构函数:清理资源 ~AudioConfigReader() = default; // 核心函数:解析XML文件(返回是否成功) bool LoadFromFile(const std::string &xml_path) { // 1. 读取XML文件内容 std::ifstream file(xml_path, std::ios::binary); if (!file.is_open()) { throw std::runtime_error("无法打开XML文件: " + xml_path); } std::string xml_content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); file.close(); // 2. 解析XML rapidxml::xml_document<> doc; try { doc.parse(&xml_content[0]); } catch (const rapidxml::parse_error &e) { throw std::runtime_error("XML解析错误: " + std::string(e.what()) + "(位置: " + std::string(e.where()) + ")"); } // 3. 获取根节点 rapidxml::xml_node<> *root = doc.first_node("AudioTagDatabase"); if (!root) { throw std::runtime_error("XML根节点错误: 找不到AudioTagDatabase"); } // 4. 解析所有节点 ParseEffects(root); // 解析 ParseRandomGroups(root); // 解析 ParseMusics(root); // 解析 return true; } // ------------------------------ // 接口1:获取音效配置() // ------------------------------ bool HasEffect(const std::string &effect_id) const { return m_effects.find(effect_id) != m_effects.end(); } const SoundEffect &GetEffect(const std::string &effect_id) const { auto it = m_effects.find(effect_id); if (it == m_effects.end()) { throw std::out_of_range("音效ID不存在: " + effect_id); } return it->second; } // 获取所有音效ID列表 std::vector GetAllEffectIds() const { return GetAllKeys(m_effects); } // ------------------------------ // 接口2:获取随机音效组配置() // ------------------------------ bool HasRandomGroup(const std::string &group_id) const { return m_random_groups.find(group_id) != m_random_groups.end(); } const RandomSoundGroup &GetRandomGroup(const std::string &group_id) const { auto it = m_random_groups.find(group_id); if (it == m_random_groups.end()) { throw std::out_of_range("随机组ID不存在: " + group_id); } return it->second; } // 获取所有随机组ID列表 std::vector GetAllRandomGroupIds() const { return GetAllKeys(m_random_groups); } // ------------------------------ // 接口3:获取背景音乐配置() // ------------------------------ bool HasMusic(const std::string &music_id) const { return m_musics.find(music_id) != m_musics.end(); } const Music &GetMusic(const std::string &music_id) const { auto it = m_musics.find(music_id); if (it == m_musics.end()) { throw std::out_of_range("背景音乐ID不存在: " + music_id); } return it->second; } // 获取所有背景音乐ID列表 std::vector GetAllMusicIds() const { return GetAllKeys(m_musics); } private: // 辅助函数:解析节点 void ParseEffects(rapidxml::xml_node<> *root) { for (rapidxml::xml_node<> *node = root->first_node("EFFECT"); node; node = node->next_sibling("EFFECT")) { // 必选属性:ID和FILE rapidxml::xml_attribute<> *id_attr = node->first_attribute("ID"); rapidxml::xml_attribute<> *file_attr = node->first_attribute("FILE"); if (!id_attr || !file_attr) { continue; // 跳过无效节点 } SoundEffect effect; effect.file_path = NormalizePath(file_attr->value()); // 统一路径分隔符 // 可选属性:VOLUME_ADJUST if (auto attr = node->first_attribute("VOLUME_ADJUST")) { effect.volume_adjust = std::stoi(attr->value()); } // 可选属性:PRIORITY if (auto attr = node->first_attribute("PRIORITY")) { effect.priority = std::stoi(attr->value()); } m_effects[id_attr->value()] = effect; } } // 辅助函数:解析节点 void ParseRandomGroups(rapidxml::xml_node<> *root) { for (rapidxml::xml_node<> *node = root->first_node("RANDOM"); node; node = node->next_sibling("RANDOM")) { // 必选属性:ID rapidxml::xml_attribute<> *id_attr = node->first_attribute("ID"); if (!id_attr) { continue; // 跳过无效节点 } RandomSoundGroup group; // 可选属性:延迟相关 if (auto attr = node->first_attribute("DELAY")) { group.delay = std::stoi(attr->value()); } if (auto attr = node->first_attribute("LOOP_DELAY")) { group.loop_delay = std::stoi(attr->value()); } if (auto attr = node->first_attribute("LOOP_DELAY_RANGE")) { group.loop_delay_range = std::stoi(attr->value()); } // 解析子节点 for (rapidxml::xml_node<> *item_node = node->first_node("ITEM"); item_node; item_node = item_node->next_sibling("ITEM")) { rapidxml::xml_attribute<> *tag_attr = item_node->first_attribute("TAG"); rapidxml::xml_attribute<> *prob_attr = item_node->first_attribute("PROB"); if (!tag_attr || !prob_attr) { continue; // 跳过无效项 } group.items.push_back({tag_attr->value(), std::stoi(prob_attr->value())}); } m_random_groups[id_attr->value()] = group; } } // 辅助函数:解析节点 void ParseMusics(rapidxml::xml_node<> *root) { for (rapidxml::xml_node<> *node = root->first_node("MUSIC"); node; node = node->next_sibling("MUSIC")) { // 必选属性:ID和FILE rapidxml::xml_attribute<> *id_attr = node->first_attribute("ID"); rapidxml::xml_attribute<> *file_attr = node->first_attribute("FILE"); if (!id_attr || !file_attr) { continue; // 跳过无效节点 } Music music; music.file_path = NormalizePath(file_attr->value()); // 统一路径分隔符 // 可选属性:LOOP_DELAY if (auto attr = node->first_attribute("LOOP_DELAY")) { music.loop_delay = std::stoi(attr->value()); } m_musics[id_attr->value()] = music; } } // 辅助函数:统一路径分隔符(将\转为/,兼容跨平台) std::string NormalizePath(const std::string &path) { std::string normalized = path; for (char &c : normalized) { if (c == '\\') { c = '/'; } } return normalized; } // 辅助函数:获取哈希表中所有key template std::vector GetAllKeys(const Map &map) const { std::vector keys; keys.reserve(map.size()); for (const auto &pair : map) { keys.push_back(pair.first); } return keys; } private: // 存储解析后的数据 std::unordered_map m_effects; // 音效映射(ID -> 音效) std::unordered_map m_random_groups; // 随机组映射(ID -> 随机组) std::unordered_map m_musics; // 背景音乐映射(ID -> 音乐) };