// ==========================================================================================
// GameFrameX 组织及其衍生项目的版权、商标、专利及其他相关权利
// GameFrameX organization and its derivative projects' copyrights, trademarks, patents, and related rights
// 均受中华人民共和国及相关国际法律法规保护。
// are protected by the laws of the People's Republic of China and related international regulations.
// 使用本项目须严格遵守相应法律法规之开源许可证之规定。
// Usage of this project must strictly comply with applicable laws, regulations, and open-source licenses.
// 本项目采用 Apache License 2.0 单协议分发,
// This project is licensed solely under the Apache License 2.0,
// 完整许可证文本请参见源代码根目录下的 LICENSE 文件。
// please see the LICENSE file in the root directory of the source code for the full license text.
// 禁止利用本项目实施任何危害国家安全、破坏社会秩序、
// It is prohibited to use this project to engage in any activities that endanger national security, disrupt social order,
// 侵犯他人合法权益等法律法规所禁止的行为!
// or infringe upon the legitimate rights or interests of others, as prohibited by laws and regulations.
// 因基于本项目二次开发所产生的一切法律纠纷与责任,
// Any legal disputes or liabilities arising from secondary development based on this project
// 本项目组织与贡献者概不承担。
// shall be borne solely by the developer; the project organization and contributors assume no responsibility.
// GitHub 仓库:https://github.com/GameFrameX
// GitHub Repository: https://github.com/GameFrameX
// Gitee 仓库:https://gitee.com/GameFrameX
// Gitee Repository: https://gitee.com/GameFrameX
// CNB 仓库:https://cnb.cool/GameFrameX
// CNB Repository: https://cnb.cool/GameFrameX
// 官方文档:https://gameframex.doc.alianblank.com/
// Official Documentation: https://gameframex.doc.alianblank.com/
// ==========================================================================================
namespace GameFrameX.Core.Hotfix;
///
/// 热更装载参数对象:承载版本、目录与文件名三要素,集中校验并单点拼装装载路径。
/// Hotfix load options: carries the version / directory / file-name triple, centralizing validation and
/// resolving the load path in one place.
///
///
/// 取代历史的 dllVersion / dllPath / hotfixDllName 三散参形态:校验与路径拼装收敛到
/// 单点(历史在两个装载入口各拼一遍),后续扩展字段(宽限覆盖、
/// 哈希校验等)不再破坏调用方签名。
/// Replaces the historical dllVersion / dllPath / hotfixDllName loose-parameter shape:
/// validation and path composition converge into (historically duplicated
/// in both load entries), and future fields (grace override, hash verification, ...) won't break caller signatures.
///
/// 路径语义与历史逐字节一致: 为空(含 null)时装载
/// {CurrentDirectory}/{AssemblyDirectory}/{AssemblyFileName};非空时以 Version 整体替代
/// 目录段({CurrentDirectory}/{Version}/{AssemblyFileName})。
/// Path semantics stay byte-identical to history: with an empty (or null) the load path is
/// {CurrentDirectory}/{AssemblyDirectory}/{AssemblyFileName}; when non-empty, Version replaces the
/// directory segment as a whole ({CurrentDirectory}/{Version}/{AssemblyFileName}).
///
///
/// 会经 HTTP 热更/回滚请求流入,属路径注入面:
/// 拒绝含路径分隔符或「..」上跳段的版本值(校验为集中化的容错面,属装载入口唯一新增行为)。
/// flows in via HTTP reload/rollback requests and is a path-injection surface:
/// rejects values containing path separators or the ".." parent segment
/// (this centralized check is the load entry's only newly-added behavior).
///
///
public sealed class HotfixLoadOptions
{
///
/// 版本目录名(默认空):非空时整体替代 作为装载目录段。
/// The version directory name (empty by default): when non-empty it replaces as the load directory segment.
///
public string Version { get; init; } = string.Empty;
///
/// 装载目录(默认 "hotfix",相对当前工作目录)。
/// The load directory (default "hotfix", relative to the current working directory).
///
public string AssemblyDirectory { get; init; } = "hotfix";
///
/// 热更程序集文件名(默认 "GameFrameX.Hotfix.dll")。
/// The hotfix assembly file name (default "GameFrameX.Hotfix.dll").
///
public string AssemblyFileName { get; init; } = "GameFrameX.Hotfix.dll";
///
/// 期望的热更程序集 SHA-256(十六进制,默认空 = 不校验)。
/// The expected SHA-256 of the hotfix assembly (hexadecimal; empty by default = no verification).
///
///
/// 仅当 AppSetting.HotfixVerifyEnabled 灰度开关开启且本值非空时,HotfixModule.Init
/// 才在创建可收集上下文与装载前流式校验文件内容,不匹配即拒绝装载。
/// Only when the AppSetting.HotfixVerifyEnabled gray switch is on and this value is non-empty
/// does HotfixModule.Init stream-verify the file content before creating the collectible
/// context and loading; a mismatch rejects the load.
///
public string ExpectedSha256 { get; init; } = string.Empty;
///
/// 版本值禁止出现的路径相关字符(分隔符与卷分隔符)。
/// Path-relevant characters (separators and the volume separator) forbidden in version values.
///
private static readonly char[] ForbiddenVersionChars = { '/', '\\', ':', };
///
/// 解析热更程序集完整装载路径(语义见类备注;含集中校验)。
/// Resolves the full load path of the hotfix assembly (semantics in class remarks; validation included).
///
/// 完整装载路径 / The full load path.
///
/// 或 为空;或 含路径分隔符 / 「..」段。
/// or is empty; or contains path separators / a ".." segment.
///
public string ResolveAssemblyFilePath()
{
ArgumentException.ThrowIfNullOrEmpty(AssemblyDirectory);
ArgumentException.ThrowIfNullOrEmpty(AssemblyFileName);
var directory = AssemblyDirectory;
if (!string.IsNullOrEmpty(Version))
{
// 版本值来自 HTTP 回滚流,拒绝路径分隔符与「..」上跳段,防装载路径被注入逃逸
// The version flows in from HTTP rollback traffic: reject separators and ".." so the load path cannot be escaped
if (Version.IndexOfAny(ForbiddenVersionChars) >= 0 || Version.Contains("..", StringComparison.Ordinal))
{
throw new ArgumentException($"Version 含非法路径字符: {Version} / Version contains forbidden path characters: {Version}", nameof(Version));
}
directory = Version;
}
return Path.Combine(Environment.CurrentDirectory, directory, AssemblyFileName);
}
}