using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Model;
namespace Milimoe.FunGame.Core.Api.Utility
{
///
/// 视觉小说文本配置器
/// 文件会保存为:程序目录/(通常是 novels)//.json
///
///
/// 新建一个配置文件,文件会保存为:程序目录/(通常是 novels)//.json
///
///
///
public class NovelConfig(string novel_name, string file_name) : Dictionary
{
///
/// 配置文件存放的根目录
///
public static string RootPath { get; set; } = "novels";
///
/// 是否允许
///
public bool Readonly { get; set; } = false;
///
/// 模组的名称
///
public string NovelName { get; set; } = novel_name;
///
/// 配置文件的名称(后缀将是.json)
///
public string FileName { get; set; } = file_name;
///
/// 断言方法字典
/// 和 都有显示的条件,反序列化 json 文件时,会根据其名称来分配具体的断言方法
///
public Dictionary> Predicates { get; set; } = [];
///
/// 使用索引器给指定key赋值,不存在key会新增
///
///
///
public new NovelNode this[string key]
{
get => base[key];
set
{
if (value != null) Add(key, value);
}
}
///
/// 获取指定key的value
///
///
///
public NovelNode? Get(string key)
{
if (TryGetValue(key, out NovelNode? value) && value != null)
{
return value;
}
return null;
}
///
/// 添加一个配置,如果已存在key会覆盖
///
///
///
public new void Add(string key, NovelNode value) => base[key] = value;
///
/// 从指定路径加载配置文件,并根据其文件名,转换为本框架所需的文件
/// 为 false 时将不会复制此文件至配置文件目录并且不允许
/// 需要注意: 用于检查加载的文件名是否在配置文件目录中已经存在
/// 如果不使用此检查,使用 时可能会覆盖原有文件(程序目录/(通常是 novels)//[所选的文件名].json)
///
///
///
///
///
///
///
///
///
///
public static NovelConfig LoadFrom(string path, string novelName, bool copyToRootPath = true, bool checkConflict = true, Dictionary>? predicates = null)
{
if (!File.Exists(path))
{
throw new FileNotFoundException($"找不到文件:{path}");
}
string fileName = Path.GetFileNameWithoutExtension(path);
NovelConfig config;
if (copyToRootPath)
{
string dpath = $@"{AppDomain.CurrentDomain.BaseDirectory}{RootPath}/{novelName}";
string fpath = $@"{dpath}/{fileName}.json";
if (checkConflict && File.Exists(fpath))
{
throw new InvalidOperationException($"文件 {fileName}.json 已存在,请先重命名。");
}
// 确保目录存在
ExistsDirectoryAndCreate(novelName);
// 复制文件内容
string json = File.ReadAllText(path, General.DefaultEncoding);
if (NetworkUtility.JsonDeserialize>(json) is null)
{
throw new InvalidDataException($"文件 {path} 内容为空或格式不正确。");
}
File.WriteAllText(fpath, json, General.DefaultEncoding);
config = new(novelName, fileName);
config.LoadConfig(predicates);
}
else
{
// 从新文件加载配置
config = new(novelName, fileName)
{
Readonly = true
};
string json = File.ReadAllText(path, General.DefaultEncoding);
Dictionary dict = NetworkUtility.JsonDeserialize>(json) ?? [];
config.LoadConfig(dict, predicates);
}
return config;
}
///
/// 从配置文件中读取配置。
///
/// 传入定义好的条件字典
public void LoadConfig(Dictionary>? predicates = null)
{
string dpath = $@"{AppDomain.CurrentDomain.BaseDirectory}{RootPath}/{NovelName}";
string fpath = $@"{dpath}/{FileName}.json";
if (Directory.Exists(dpath) && File.Exists(fpath))
{
string json = File.ReadAllText(fpath, General.DefaultEncoding);
Dictionary dict = NetworkUtility.JsonDeserialize>(json) ?? [];
LoadConfig(dict, predicates);
}
}
///
/// 根据断言方法字典重新生成小说的字典
///
///
///
private void LoadConfig(Dictionary dict, Dictionary>? predicates = null)
{
if (predicates != null)
{
foreach (string key in predicates.Keys)
{
// 直接覆盖
Predicates[key] = predicates[key];
}
}
Clear();
foreach (string key in dict.Keys)
{
NovelNode obj = dict[key];
base.Add(key, obj);
if (obj.Values.TryGetValue(nameof(NovelNode.Previous), out object? value) && value is string prevKey && dict.Values.FirstOrDefault(n => n.Key == prevKey) is NovelNode prev)
{
obj.Previous = prev;
}
if (obj.Values.TryGetValue(nameof(NovelNode.NextNodes), out value) && value is List nextKeys)
{
foreach (string nextKey in nextKeys)
{
if (dict.TryGetValue(nextKey, out NovelNode? node) && node != null)
{
obj.NextNodes.Add(node);
}
}
}
if (Predicates != null)
{
if (obj.Values.TryGetValue(nameof(NovelNode.AndPredicates), out object? value2) && value2 is List aps)
{
foreach (string ap in aps)
{
if (Predicates.TryGetValue(ap, out Func? value3) && value3 != null)
{
obj.AndPredicates[ap] = value3;
}
}
}
if (obj.Values.TryGetValue(nameof(NovelNode.OrPredicates), out value2) && value2 is List ops)
{
foreach (string op in ops)
{
if (Predicates.TryGetValue(op, out Func? value3) && value3 != null)
{
obj.OrPredicates[op] = value3;
}
}
}
}
foreach (NovelOption option in obj.Options)
{
if (option.Values.TryGetValue(nameof(NovelOption.Targets), out object? value2) && value2 is List targets)
{
foreach (string targetKey in targets)
{
if (dict.TryGetValue(targetKey, out NovelNode? node) && node != null)
{
option.Targets.Add(node);
}
}
}
if (Predicates != null)
{
if (option.Values.TryGetValue(nameof(NovelNode.AndPredicates), out object? value3) && value3 is List aps)
{
foreach (string ap in aps)
{
if (Predicates.TryGetValue(ap, out Func? value4) && value4 != null)
{
option.AndPredicates[ap] = value4;
}
}
}
if (option.Values.TryGetValue(nameof(NovelNode.OrPredicates), out value3) && value3 is List ops)
{
foreach (string op in ops)
{
if (Predicates.TryGetValue(op, out Func? value4) && value4 != null)
{
option.OrPredicates[op] = value4;
}
}
}
}
}
}
}
///
/// 将配置保存到配置文件。调用此方法会覆盖原有的.json,请注意备份
///
public void SaveConfig()
{
if (Readonly)
{
return;
}
string json = NetworkUtility.JsonSerialize((Dictionary)this);
string dpath = $@"{AppDomain.CurrentDomain.BaseDirectory}{RootPath}/{NovelName}";
string fpath = $@"{dpath}/{FileName}.json";
if (!Directory.Exists(dpath))
{
Directory.CreateDirectory(dpath);
}
using StreamWriter writer = new(fpath, false, General.DefaultEncoding);
writer.WriteLine(json);
writer.Flush();
}
///
/// 检查配置文件目录是否存在
///
///
///
public static bool ExistsDirectory(string novelName)
{
string dpath = $@"{AppDomain.CurrentDomain.BaseDirectory}{RootPath}/{novelName}";
return Directory.Exists(dpath);
}
///
/// 检查配置文件目录是否存在,不存在则创建
///
///
///
public static bool ExistsDirectoryAndCreate(string novelName)
{
string dpath = $@"{AppDomain.CurrentDomain.BaseDirectory}{RootPath}/{novelName}";
bool result = Directory.Exists(dpath);
if (!result)
{
Directory.CreateDirectory(dpath);
}
return result;
}
///
/// 检查配置文件目录中是否存在指定文件
///
///
///
///
public static bool ExistsFile(string novelName, string fileName)
{
string dpath = $@"{AppDomain.CurrentDomain.BaseDirectory}{RootPath}/{novelName}";
string fpath = $@"{dpath}/{fileName}.json";
return File.Exists(fpath);
}
}
}