Feature: Embed language resources, disk files act as optional patches

This commit is contained in:
yuanyuanxiang
2026-05-05 13:22:47 +02:00
parent 773c78ac0f
commit f11fc93ba8
5 changed files with 235 additions and 61 deletions

View File

@@ -50,47 +50,71 @@ public:
char line[4096];
while (fgets(line, sizeof(line), f)) {
// 去除行尾换行符
size_t len = strlen(line);
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
line[--len] = '\0';
ParseLine(line, currentSection);
}
if (len == 0)
continue;
fclose(f);
return true;
}
// 跳过注释
if (line[0] == ';' || line[0] == '#')
continue;
// 从内存加载 INI 数据,返回是否成功
// 用于加载嵌入的资源数据
bool LoadFromMemory(const char* data, size_t size)
{
Clear();
// 检测 section 头: [SectionName]
// 真正的 section 头:']' 后面没有 '='(否则是 key=value
if (line[0] == '[') {
char* end = strchr(line, ']');
if (end) {
char* eqAfter = strchr(end + 1, '=');
if (!eqAfter) {
// 纯 section 头,如 [Strings]
*end = '\0';
currentSection = line + 1;
continue;
}
// ']' 后有 '=',如 [使用FRP]=[Using FRP],当作 key=value 处理
}
if (!data || size == 0)
return false;
std::string currentSection;
const char* p = data;
const char* end = data + size;
while (p < end) {
// 找到行尾
const char* lineEnd = p;
while (lineEnd < end && *lineEnd != '\n' && *lineEnd != '\r')
lineEnd++;
// 复制行内容
size_t lineLen = lineEnd - p;
if (lineLen > 0 && lineLen < 4096) {
char line[4096];
memcpy(line, p, lineLen);
line[lineLen] = '\0';
ParseLine(line, currentSection);
}
// 不在任何 section 内则跳过
if (currentSection.empty())
continue;
// 跳过换行符
p = lineEnd;
while (p < end && (*p == '\n' || *p == '\r'))
p++;
}
// 解析 key=value只按第一个 '=' 分割,不 trim
// key 和 value 均做反转义(\n \r \t \\ \"
char* eq = strchr(line, '=');
if (eq && eq != line) {
*eq = '\0';
std::string key = Unescape(std::string(line));
std::string value = Unescape(std::string(eq + 1));
m_sections[currentSection][key] = value;
}
return true;
}
// 追加加载(不清除现有数据,用于覆盖)
bool LoadFileAppend(const char* filePath)
{
if (!filePath || !filePath[0])
return false;
FILE* f = nullptr;
#ifdef _MSC_VER
if (fopen_s(&f, filePath, "r") != 0 || !f)
return false;
#else
f = fopen(filePath, "r");
if (!f)
return false;
#endif
std::string currentSection;
char line[4096];
while (fgets(line, sizeof(line), f)) {
ParseLine(line, currentSection);
}
fclose(f);
@@ -138,6 +162,52 @@ public:
private:
TSections m_sections;
// 解析单行 INI 内容
void ParseLine(char* line, std::string& currentSection)
{
// 去除行尾换行符
size_t len = strlen(line);
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
line[--len] = '\0';
if (len == 0)
return;
// 跳过注释
if (line[0] == ';' || line[0] == '#')
return;
// 检测 section 头: [SectionName]
// 真正的 section 头:']' 后面没有 '='(否则是 key=value
if (line[0] == '[') {
char* end = strchr(line, ']');
if (end) {
char* eqAfter = strchr(end + 1, '=');
if (!eqAfter) {
// 纯 section 头,如 [Strings]
*end = '\0';
currentSection = line + 1;
return;
}
// ']' 后有 '=',如 [使用FRP]=[Using FRP],当作 key=value 处理
}
}
// 不在任何 section 内则跳过
if (currentSection.empty())
return;
// 解析 key=value只按第一个 '=' 分割,不 trim
// key 和 value 均做反转义(\n \r \t \\ \"
char* eq = strchr(line, '=');
if (eq && eq != line) {
*eq = '\0';
std::string key = Unescape(std::string(line));
std::string value = Unescape(std::string(eq + 1));
m_sections[currentSection][key] = value;
}
}
// 反转义:将字面量 \n \r \t \\ \" 转为对应的控制字符
static std::string Unescape(const std::string& s)
{