306 lines
9.4 KiB
C++
306 lines
9.4 KiB
C++
#pragma once
|
||
#include <string>
|
||
#include <unordered_map>
|
||
#include <vector>
|
||
#include <fstream>
|
||
#include <stdexcept>
|
||
#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<RandomItem> 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<char>(file)),
|
||
std::istreambuf_iterator<char>());
|
||
file.close();
|
||
|
||
// 2. 解析XML
|
||
rapidxml::xml_document<> doc;
|
||
try
|
||
{
|
||
doc.parse<rapidxml::parse_default>(&xml_content[0]);
|
||
}
|
||
catch (const rapidxml::parse_error &e)
|
||
{
|
||
throw std::runtime_error("XML解析错误: " + std::string(e.what()) +
|
||
"(位置: " + std::string(e.where<char>()) + ")");
|
||
}
|
||
|
||
// 3. 获取根节点
|
||
rapidxml::xml_node<> *root = doc.first_node("AudioTagDatabase");
|
||
if (!root)
|
||
{
|
||
throw std::runtime_error("XML根节点错误: 找不到AudioTagDatabase");
|
||
}
|
||
|
||
// 4. 解析所有节点
|
||
ParseEffects(root); // 解析<EFFECT>
|
||
ParseRandomGroups(root); // 解析<RANDOM>
|
||
ParseMusics(root); // 解析<MUSIC>
|
||
|
||
return true;
|
||
}
|
||
|
||
// ------------------------------
|
||
// 接口1:获取音效配置(<EFFECT>)
|
||
// ------------------------------
|
||
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<std::string> GetAllEffectIds() const
|
||
{
|
||
return GetAllKeys(m_effects);
|
||
}
|
||
|
||
// ------------------------------
|
||
// 接口2:获取随机音效组配置(<RANDOM>)
|
||
// ------------------------------
|
||
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<std::string> GetAllRandomGroupIds() const
|
||
{
|
||
return GetAllKeys(m_random_groups);
|
||
}
|
||
|
||
// ------------------------------
|
||
// 接口3:获取背景音乐配置(<MUSIC>)
|
||
// ------------------------------
|
||
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<std::string> GetAllMusicIds() const
|
||
{
|
||
return GetAllKeys(m_musics);
|
||
}
|
||
|
||
private:
|
||
// 辅助函数:解析<EFFECT>节点
|
||
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;
|
||
}
|
||
}
|
||
|
||
// 辅助函数:解析<RANDOM>节点
|
||
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());
|
||
}
|
||
|
||
// 解析子节点<ITEM>
|
||
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;
|
||
}
|
||
}
|
||
|
||
// 辅助函数:解析<MUSIC>节点
|
||
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 <typename Map>
|
||
std::vector<std::string> GetAllKeys(const Map &map) const
|
||
{
|
||
std::vector<std::string> keys;
|
||
keys.reserve(map.size());
|
||
for (const auto &pair : map)
|
||
{
|
||
keys.push_back(pair.first);
|
||
}
|
||
return keys;
|
||
}
|
||
|
||
private:
|
||
// 存储解析后的数据
|
||
std::unordered_map<std::string, SoundEffect> m_effects; // 音效映射(ID -> 音效)
|
||
std::unordered_map<std::string, RandomSoundGroup> m_random_groups; // 随机组映射(ID -> 随机组)
|
||
std::unordered_map<std::string, Music> m_musics; // 背景音乐映射(ID -> 音乐)
|
||
}; |