This commit is contained in:
2026-02-08 16:20:50 +08:00
parent 0ae47e5d6a
commit 8b88904ef7
72 changed files with 5963 additions and 2038 deletions

View File

@@ -0,0 +1,306 @@
#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; // 关联的音效IDTAG属性
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 -> 音乐)
};

View File

@@ -0,0 +1,233 @@
#include "Asset_SoundPack.h"
Asset_SoundPack::Asset_SoundPack()
{
int device = -1; // 使用默认设备
unsigned long freq = 44100; // 采样频率PCM常用的采样率之一可以按需调整
// 构造NPK信息
InitNpkList();
// 解析audio.xml
XmlConfig.LoadFromFile("audio.xml");
// 初始化SDL_mixer支持OGG格式
// 44100: 采样率, MIX_DEFAULT_FORMAT: 音频格式, 2: 声道数(立体声), 4096: 缓冲区大小
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 8192) < 0)
{
SDL_LogError(0, "SDL_mixer初始化失败! Mix_Error: %s\n", Mix_GetError());
}
if (Mix_Init(MIX_INIT_MP3 | MIX_INIT_OGG) < 0)
{
SDL_LogError(0, "Mix_Init failed: %s\n", Mix_GetError());
}
// 分配通道
ChannelCount = Mix_AllocateChannels(64);
SetAllChannelVolume(0);
}
Asset_SoundPack::~Asset_SoundPack()
{
}
void Asset_SoundPack::InitNpkList()
{
DIR *dir;
struct dirent *ent;
std::string path = "SoundPacks/";
dir = opendir(path.c_str());
while ((ent = readdir(dir)))
{
// 跳过.和..目录
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
continue;
std::string RealPath = path + ent->d_name;
Ifstream_NPK Fs;
Fs.open(RealPath.c_str(), std::ios::in | std::ios::binary);
if (Fs.is_open())
{
std::string Header = Fs.ReadString();
// 如果是NPK
if (Header == "NeoplePack_Bill")
{
// 读取img数量
int ImageCount = Fs.ReadInt();
for (int i = 0; i < ImageCount; ++i)
{
// 直接读取当前项的偏移、长度和路径
auto offset = Fs.ReadUnsignedInt();
auto length = Fs.ReadUnsignedInt();
auto path = Fs.ReadInfo();
// 构造 WAV 并插入映射表
lp_arr[path] = WAV{
offset, // offset
length, // size
path, // lpWavName直接复用读取的path
RealPath, // lpBelongsFile
i // img_index
};
}
}
}
}
closedir(dir);
}
void Asset_SoundPack::Reclaims()
{
}
Mix_Chunk *Asset_SoundPack::GetOgg(const std::string &lpWavName)
{
if (map_sound.find(lpWavName) != map_sound.end())
{
map_sound[lpWavName].LastUseTime = SDL_GetTicks64();
return map_sound[lpWavName].lpMusic;
}
// 在NPK映射表中找到
if (lp_arr.find(lpWavName) != lp_arr.end())
{
WAV &wav = lp_arr[lpWavName];
if (wav.lpBelongsFile.empty())
return nullptr;
Ifstream_NPK Fs;
Fs.open(wav.lpBelongsFile.c_str(), std::ios::in | std::ios::binary);
if (Fs.is_open())
{
Fs.seekg(wav.offset, std::ios::beg);
auto buffer = Fs.ReadCustomSize(wav.size);
SDL_RWops *rw = SDL_RWFromConstMem(buffer, wav.size);
if (!rw)
{
SDL_Log("创建内存流失败: %s\n", SDL_GetError());
Mix_CloseAudio();
SDL_Quit();
return nullptr;
}
Mix_Chunk *music = Mix_LoadWAV_RW(rw, 0);
if (!music)
{
SDL_Log("加载OGG失败: %s\n", Mix_GetError());
SDL_RWclose(rw); // 手动关闭流
Mix_CloseAudio();
SDL_Quit();
return nullptr;
}
map_sound[lpWavName].lpMusic = music;
map_sound[lpWavName].LastUseTime = SDL_GetTicks64();
return music;
}
}
// 去Music文件夹中寻找
else
{
Mix_Chunk *music = Mix_LoadWAV(lpWavName.c_str());
if (!music)
{
SDL_Log("加载OGG失败: %s\n", Mix_GetError());
Mix_CloseAudio();
SDL_Quit();
return nullptr;
}
map_sound[lpWavName].lpMusic = music;
map_sound[lpWavName].LastUseTime = SDL_GetTicks64();
return music;
}
}
void Asset_SoundPack::PlayAudio(const std::string Key)
{
std::string RealKey = Key;
// 先判断是不是随机组 如果是随机组去里面调出真正要播放的Key
if (XmlConfig.HasRandomGroup(Key))
{
auto Group = XmlConfig.GetRandomGroup(Key);
int RandNum = rand() % 100;
for (auto &item : Group.items)
{
if (RandNum <= item.prob)
{
RealKey = item.tag;
}
}
}
// 如果是特效音频
if (XmlConfig.HasEffect(Key))
{
std::string oggPath = XmlConfig.GetEffect(Key).file_path;
int volume = XmlConfig.GetEffect(Key).volume_adjust;
PlayEffect(oggPath, volume);
}
// 如果是背景音乐
else if (XmlConfig.HasMusic(Key))
{
std::string oggPath = XmlConfig.GetMusic(Key).file_path;
PlayMusic(Key, oggPath);
}
else
{
SDL_LogError(0, "找不到音频: %s\n", Key.c_str());
}
}
void Asset_SoundPack::StopAudio(const std::string Key)
{
StopChannel(backgroud_music_map[Key]);
backgroud_music_map.erase(Key);
}
void Asset_SoundPack::PlayMusic(const std::string Key, const std::string oggpath)
{
auto ogg = GetOgg(oggpath);
if (ogg)
{
int channel = FindFreeChannelInRange(0, 4);
backgroud_music_map[Key] = Mix_PlayChannel(channel, ogg, -1);
}
}
void Asset_SoundPack::PlayEffect(const std::string Key, int Volume)
{
auto ogg = GetOgg(Key);
if (ogg)
{
int channel = FindFreeChannelInRange(4, ChannelCount - 1);
Mix_VolumeChunk(ogg, Volume);
Mix_PlayChannel(channel, ogg, 0);
}
}
void Asset_SoundPack::StopChannel(int Channel)
{
Mix_HaltChannel(Channel); // 停止指定通道的音效
}
void Asset_SoundPack::SetAllChannelVolume(int Volume)
{
Mix_Volume(-1, Volume);
}
std::map<std::string, int> Asset_SoundPack::GetBackgroudMusicVector()
{
return backgroud_music_map;
}
int Asset_SoundPack::FindFreeChannelInRange(int startChannel, int endChannel)
{
// 遍历通道若总通道数小于32则只检查到最后一个通道
for (int channel = startChannel; channel <= endChannel; channel++)
{
// 检查通道是否空闲Mix_Playing返回0表示空闲
if (Mix_Playing(channel) == 0)
{
return channel; // 返回第一个空闲通道
}
}
// 若所有通道都在使用中
return -1;
}

View File

@@ -0,0 +1,88 @@
#pragma once
#include <string>
#include <dirent.h>
#include <map>
#include <stdio.h>
#include <SDL.h>
#include <SDL_mixer.h>
#include "Tool/Ifstream_NPK.h"
#include "Asset/Asset_Audio.hpp"
class Asset_SoundPack
{
public:
struct WAV
{
unsigned int offset; // 偏移
unsigned int size; // 大小
std::string lpWavName; // img文件的路径
std::string lpBelongsFile; // 这个img属于哪个npk文件
int img_index; // img文件在npk文件里的序号
};
struct NpkInfo
{
int Offset;
int Length;
std::string Path;
};
struct MusicInfo
{
/**上次使用时间 */
Uint64 LastUseTime;
/**音频指针 */
Mix_Chunk *lpMusic;
};
public:
Asset_SoundPack(const Asset_SoundPack &) = delete;
Asset_SoundPack &operator=(const Asset_SoundPack &) = delete;
Asset_SoundPack(Asset_SoundPack &&) = delete;
Asset_SoundPack &operator=(Asset_SoundPack &&) = delete;
// 全局访问点
static Asset_SoundPack &GetInstance()
{
static Asset_SoundPack instance; // 局部静态变量,保证只初始化一次
return instance;
}
/**音频NPK信息表 */
std::map<std::string, WAV> lp_arr;
/**音频储存表 */
std::map<std::string, MusicInfo> map_sound;
/**audio信息表 */
AudioConfigReader XmlConfig;
/**正在播放的背景音乐表 */
std::map<std::string, int> backgroud_music_map;
/**总通道数 */
int ChannelCount;
public:
Asset_SoundPack(/* args */);
~Asset_SoundPack();
void InitNpkList();
void Reclaims();
Mix_Chunk *GetOgg(const std::string &lpWavName);
/**播放音频 */
void PlayAudio(const std::string Key);
/**停止音频 */
void StopAudio(const std::string Key);
void PlayMusic(const std::string Key, const std::string oggpath);
void PlayEffect(const std::string Key, int Volume);
/**停止通道播放音频 */
void StopChannel(int Channel);
/**设置所有通道音量 */
void SetAllChannelVolume(int Volume);
/**获取正在播放的背景音乐 */
std::map<std::string, int> GetBackgroudMusicVector();
/**获取空闲的通道 */
int FindFreeChannelInRange(int Start, int End);
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,174 @@
#ifndef RAPIDXML_ITERATORS_HPP_INCLUDED
#define RAPIDXML_ITERATORS_HPP_INCLUDED
// Copyright (C) 2006, 2009 Marcin Kalicinski
// Version 1.13
// Revision $DateTime: 2009/05/13 01:46:17 $
//! \file rapidxml_iterators.hpp This file contains rapidxml iterators
#include "rapidxml.hpp"
namespace rapidxml
{
//! Iterator of child nodes of xml_node
template<class Ch>
class node_iterator
{
public:
typedef typename xml_node<Ch> value_type;
typedef typename xml_node<Ch> &reference;
typedef typename xml_node<Ch> *pointer;
typedef std::ptrdiff_t difference_type;
typedef std::bidirectional_iterator_tag iterator_category;
node_iterator()
: m_node(0)
{
}
node_iterator(xml_node<Ch> *node)
: m_node(node->first_node())
{
}
reference operator *() const
{
assert(m_node);
return *m_node;
}
pointer operator->() const
{
assert(m_node);
return m_node;
}
node_iterator& operator++()
{
assert(m_node);
m_node = m_node->next_sibling();
return *this;
}
node_iterator operator++(int)
{
node_iterator tmp = *this;
++this;
return tmp;
}
node_iterator& operator--()
{
assert(m_node && m_node->previous_sibling());
m_node = m_node->previous_sibling();
return *this;
}
node_iterator operator--(int)
{
node_iterator tmp = *this;
++this;
return tmp;
}
bool operator ==(const node_iterator<Ch> &rhs)
{
return m_node == rhs.m_node;
}
bool operator !=(const node_iterator<Ch> &rhs)
{
return m_node != rhs.m_node;
}
private:
xml_node<Ch> *m_node;
};
//! Iterator of child attributes of xml_node
template<class Ch>
class attribute_iterator
{
public:
typedef typename xml_attribute<Ch> value_type;
typedef typename xml_attribute<Ch> &reference;
typedef typename xml_attribute<Ch> *pointer;
typedef std::ptrdiff_t difference_type;
typedef std::bidirectional_iterator_tag iterator_category;
attribute_iterator()
: m_attribute(0)
{
}
attribute_iterator(xml_node<Ch> *node)
: m_attribute(node->first_attribute())
{
}
reference operator *() const
{
assert(m_attribute);
return *m_attribute;
}
pointer operator->() const
{
assert(m_attribute);
return m_attribute;
}
attribute_iterator& operator++()
{
assert(m_attribute);
m_attribute = m_attribute->next_attribute();
return *this;
}
attribute_iterator operator++(int)
{
attribute_iterator tmp = *this;
++this;
return tmp;
}
attribute_iterator& operator--()
{
assert(m_attribute && m_attribute->previous_attribute());
m_attribute = m_attribute->previous_attribute();
return *this;
}
attribute_iterator operator--(int)
{
attribute_iterator tmp = *this;
++this;
return tmp;
}
bool operator ==(const attribute_iterator<Ch> &rhs)
{
return m_attribute == rhs.m_attribute;
}
bool operator !=(const attribute_iterator<Ch> &rhs)
{
return m_attribute != rhs.m_attribute;
}
private:
xml_attribute<Ch> *m_attribute;
};
}
#endif

View File

@@ -0,0 +1,421 @@
#ifndef RAPIDXML_PRINT_HPP_INCLUDED
#define RAPIDXML_PRINT_HPP_INCLUDED
// Copyright (C) 2006, 2009 Marcin Kalicinski
// Version 1.13
// Revision $DateTime: 2009/05/13 01:46:17 $
//! \file rapidxml_print.hpp This file contains rapidxml printer implementation
#include "rapidxml.hpp"
// Only include streams if not disabled
#ifndef RAPIDXML_NO_STREAMS
#include <ostream>
#include <iterator>
#endif
namespace rapidxml
{
///////////////////////////////////////////////////////////////////////
// Printing flags
const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function.
///////////////////////////////////////////////////////////////////////
// Internal
//! \cond internal
namespace internal
{
///////////////////////////////////////////////////////////////////////////
// Internal character operations
// Copy characters from given range to given output iterator
template<class OutIt, class Ch>
inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out)
{
while (begin != end)
*out++ = *begin++;
return out;
}
// Copy characters from given range to given output iterator and expand
// characters into references (&lt; &gt; &apos; &quot; &amp;)
template<class OutIt, class Ch>
inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out)
{
while (begin != end)
{
if (*begin == noexpand)
{
*out++ = *begin; // No expansion, copy character
}
else
{
switch (*begin)
{
case Ch('<'):
*out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';');
break;
case Ch('>'):
*out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';');
break;
case Ch('\''):
*out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';');
break;
case Ch('"'):
*out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';');
break;
case Ch('&'):
*out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';');
break;
default:
*out++ = *begin; // No expansion, copy character
}
}
++begin; // Step to next character
}
return out;
}
// Fill given output iterator with repetitions of the same character
template<class OutIt, class Ch>
inline OutIt fill_chars(OutIt out, int n, Ch ch)
{
for (int i = 0; i < n; ++i)
*out++ = ch;
return out;
}
// Find character
template<class Ch, Ch ch>
inline bool find_char(const Ch *begin, const Ch *end)
{
while (begin != end)
if (*begin++ == ch)
return true;
return false;
}
///////////////////////////////////////////////////////////////////////////
// Internal printing operations
// Print node
template<class OutIt, class Ch>
inline OutIt print_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
// Print proper node type
switch (node->type())
{
// Document
case node_document:
out = print_children(out, node, flags, indent);
break;
// Element
case node_element:
out = print_element_node(out, node, flags, indent);
break;
// Data
case node_data:
out = print_data_node(out, node, flags, indent);
break;
// CDATA
case node_cdata:
out = print_cdata_node(out, node, flags, indent);
break;
// Declaration
case node_declaration:
out = print_declaration_node(out, node, flags, indent);
break;
// Comment
case node_comment:
out = print_comment_node(out, node, flags, indent);
break;
// Doctype
case node_doctype:
out = print_doctype_node(out, node, flags, indent);
break;
// Pi
case node_pi:
out = print_pi_node(out, node, flags, indent);
break;
// Unknown
default:
assert(0);
break;
}
// If indenting not disabled, add line break after node
if (!(flags & print_no_indenting))
*out = Ch('\n'), ++out;
// Return modified iterator
return out;
}
// Print children of the node
template<class OutIt, class Ch>
inline OutIt print_children(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
for (xml_node<Ch> *child = node->first_node(); child; child = child->next_sibling())
out = print_node(out, child, flags, indent);
return out;
}
// Print attributes of the node
template<class OutIt, class Ch>
inline OutIt print_attributes(OutIt out, const xml_node<Ch> *node, int flags)
{
for (xml_attribute<Ch> *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute())
{
if (attribute->name() && attribute->value())
{
// Print attribute name
*out = Ch(' '), ++out;
out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out);
*out = Ch('='), ++out;
// Print attribute value using appropriate quote type
if (find_char<Ch, Ch('"')>(attribute->value(), attribute->value() + attribute->value_size()))
{
*out = Ch('\''), ++out;
out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out);
*out = Ch('\''), ++out;
}
else
{
*out = Ch('"'), ++out;
out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out);
*out = Ch('"'), ++out;
}
}
}
return out;
}
// Print data node
template<class OutIt, class Ch>
inline OutIt print_data_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_data);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
return out;
}
// Print data node
template<class OutIt, class Ch>
inline OutIt print_cdata_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_cdata);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'); ++out;
*out = Ch('!'); ++out;
*out = Ch('['); ++out;
*out = Ch('C'); ++out;
*out = Ch('D'); ++out;
*out = Ch('A'); ++out;
*out = Ch('T'); ++out;
*out = Ch('A'); ++out;
*out = Ch('['); ++out;
out = copy_chars(node->value(), node->value() + node->value_size(), out);
*out = Ch(']'); ++out;
*out = Ch(']'); ++out;
*out = Ch('>'); ++out;
return out;
}
// Print element node
template<class OutIt, class Ch>
inline OutIt print_element_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_element);
// Print element name and attributes, if any
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'), ++out;
out = copy_chars(node->name(), node->name() + node->name_size(), out);
out = print_attributes(out, node, flags);
// If node is childless
if (node->value_size() == 0 && !node->first_node())
{
// Print childless node tag ending
*out = Ch('/'), ++out;
*out = Ch('>'), ++out;
}
else
{
// Print normal node tag ending
*out = Ch('>'), ++out;
// Test if node contains a single data node only (and no other nodes)
xml_node<Ch> *child = node->first_node();
if (!child)
{
// If node has no children, only print its value without indenting
out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
}
else if (child->next_sibling() == 0 && child->type() == node_data)
{
// If node has a sole data child, only print its value without indenting
out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out);
}
else
{
// Print all children with full indenting
if (!(flags & print_no_indenting))
*out = Ch('\n'), ++out;
out = print_children(out, node, flags, indent + 1);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
}
// Print node end
*out = Ch('<'), ++out;
*out = Ch('/'), ++out;
out = copy_chars(node->name(), node->name() + node->name_size(), out);
*out = Ch('>'), ++out;
}
return out;
}
// Print declaration node
template<class OutIt, class Ch>
inline OutIt print_declaration_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
// Print declaration start
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'), ++out;
*out = Ch('?'), ++out;
*out = Ch('x'), ++out;
*out = Ch('m'), ++out;
*out = Ch('l'), ++out;
// Print attributes
out = print_attributes(out, node, flags);
// Print declaration end
*out = Ch('?'), ++out;
*out = Ch('>'), ++out;
return out;
}
// Print comment node
template<class OutIt, class Ch>
inline OutIt print_comment_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_comment);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'), ++out;
*out = Ch('!'), ++out;
*out = Ch('-'), ++out;
*out = Ch('-'), ++out;
out = copy_chars(node->value(), node->value() + node->value_size(), out);
*out = Ch('-'), ++out;
*out = Ch('-'), ++out;
*out = Ch('>'), ++out;
return out;
}
// Print doctype node
template<class OutIt, class Ch>
inline OutIt print_doctype_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_doctype);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'), ++out;
*out = Ch('!'), ++out;
*out = Ch('D'), ++out;
*out = Ch('O'), ++out;
*out = Ch('C'), ++out;
*out = Ch('T'), ++out;
*out = Ch('Y'), ++out;
*out = Ch('P'), ++out;
*out = Ch('E'), ++out;
*out = Ch(' '), ++out;
out = copy_chars(node->value(), node->value() + node->value_size(), out);
*out = Ch('>'), ++out;
return out;
}
// Print pi node
template<class OutIt, class Ch>
inline OutIt print_pi_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_pi);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'), ++out;
*out = Ch('?'), ++out;
out = copy_chars(node->name(), node->name() + node->name_size(), out);
*out = Ch(' '), ++out;
out = copy_chars(node->value(), node->value() + node->value_size(), out);
*out = Ch('?'), ++out;
*out = Ch('>'), ++out;
return out;
}
}
//! \endcond
///////////////////////////////////////////////////////////////////////////
// Printing
//! Prints XML to given output iterator.
//! \param out Output iterator to print to.
//! \param node Node to be printed. Pass xml_document to print entire document.
//! \param flags Flags controlling how XML is printed.
//! \return Output iterator pointing to position immediately after last character of printed text.
template<class OutIt, class Ch>
inline OutIt print(OutIt out, const xml_node<Ch> &node, int flags = 0)
{
return internal::print_node(out, &node, flags, 0);
}
#ifndef RAPIDXML_NO_STREAMS
//! Prints XML to given output stream.
//! \param out Output stream to print to.
//! \param node Node to be printed. Pass xml_document to print entire document.
//! \param flags Flags controlling how XML is printed.
//! \return Output stream.
template<class Ch>
inline std::basic_ostream<Ch> &print(std::basic_ostream<Ch> &out, const xml_node<Ch> &node, int flags = 0)
{
print(std::ostream_iterator<Ch>(out), node, flags);
return out;
}
//! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process.
//! \param out Output stream to print to.
//! \param node Node to be printed.
//! \return Output stream.
template<class Ch>
inline std::basic_ostream<Ch> &operator <<(std::basic_ostream<Ch> &out, const xml_node<Ch> &node)
{
return print(out, node);
}
#endif
}
#endif

View File

@@ -0,0 +1,122 @@
#ifndef RAPIDXML_UTILS_HPP_INCLUDED
#define RAPIDXML_UTILS_HPP_INCLUDED
// Copyright (C) 2006, 2009 Marcin Kalicinski
// Version 1.13
// Revision $DateTime: 2009/05/13 01:46:17 $
//! \file rapidxml_utils.hpp This file contains high-level rapidxml utilities that can be useful
//! in certain simple scenarios. They should probably not be used if maximizing performance is the main objective.
#include "rapidxml.hpp"
#include <vector>
#include <string>
#include <fstream>
#include <stdexcept>
namespace rapidxml
{
//! Represents data loaded from a file
template<class Ch = char>
class file
{
public:
//! Loads file into the memory. Data will be automatically destroyed by the destructor.
//! \param filename Filename to load.
file(const char *filename)
{
using namespace std;
// Open stream
basic_ifstream<Ch> stream(filename, ios::binary);
if (!stream)
throw runtime_error(string("cannot open file ") + filename);
stream.unsetf(ios::skipws);
// Determine stream size
stream.seekg(0, ios::end);
size_t size = stream.tellg();
stream.seekg(0);
// Load data and add terminating 0
m_data.resize(size + 1);
stream.read(&m_data.front(), static_cast<streamsize>(size));
m_data[size] = 0;
}
//! Loads file into the memory. Data will be automatically destroyed by the destructor
//! \param stream Stream to load from
file(std::basic_istream<Ch> &stream)
{
using namespace std;
// Load data and add terminating 0
stream.unsetf(ios::skipws);
m_data.assign(istreambuf_iterator<Ch>(stream), istreambuf_iterator<Ch>());
if (stream.fail() || stream.bad())
throw runtime_error("error reading stream");
m_data.push_back(0);
}
//! Gets file data.
//! \return Pointer to data of file.
Ch *data()
{
return &m_data.front();
}
//! Gets file data.
//! \return Pointer to data of file.
const Ch *data() const
{
return &m_data.front();
}
//! Gets file data size.
//! \return Size of file data, in characters.
std::size_t size() const
{
return m_data.size();
}
private:
std::vector<Ch> m_data; // File data
};
//! Counts children of node. Time complexity is O(n).
//! \return Number of children of node
template<class Ch>
inline std::size_t count_children(xml_node<Ch> *node)
{
xml_node<Ch> *child = node->first_node();
std::size_t count = 0;
while (child)
{
++count;
child = child->next_sibling();
}
return count;
}
//! Counts attributes of node. Time complexity is O(n).
//! \return Number of attributes of node
template<class Ch>
inline std::size_t count_attributes(xml_node<Ch> *node)
{
xml_attribute<Ch> *attr = node->first_attribute();
std::size_t count = 0;
while (attr)
{
++count;
attr = attr->next_attribute();
}
return count;
}
}
#endif