首页
/ CreamInstaller技术解析:多平台DLC解锁工具的架构与实现

CreamInstaller技术解析:多平台DLC解锁工具的架构与实现

2026-04-30 09:25:07作者:盛欣凯Ernestine

1. 技术架构概述

CreamInstaller作为一款跨平台DLC解锁解决方案,采用模块化架构设计,通过分层抽象实现对Steam、Epic和Ubisoft等多个游戏平台的支持。该工具的核心架构包含四个主要层次:平台抽象层、组件管理层、配置生成层和用户交互层,各层之间通过明确定义的接口进行通信,确保系统的可扩展性和可维护性。

系统采用C#语言开发,基于.NET Framework运行时环境,利用Windows API实现底层系统交互。核心代码组织在CreamInstaller命名空间下,通过Platforms子命名空间区分不同游戏平台的实现,Resources命名空间包含各类解锁组件,Utility命名空间提供通用工具函数。

2. 核心组件技术原理

2.1 Koaloader组件系统

Koaloader作为兼容性层核心组件,通过DLL代理技术实现API重定向。其工作原理是将特定系统DLL(如version.dll、dinput8.dll)替换为自定义实现,在不修改游戏原始代码的情况下拦截并修改API调用。

Koaloader组件支持32位和64位两种架构,通过以下代码实现代理DLL的动态选择与加载:

// 根据二进制类型选择合适的代理DLL
internal static void GetProxyInfoFromIdentifier(this string resourceIdentifier, out string proxyName, out BinaryType binaryType)
{
    string baseIdentifier = resourceIdentifier[(resourceIdentifier.IndexOf('.') + 1)..];
    baseIdentifier = baseIdentifier[..baseIdentifier.IndexOf('.')];
    proxyName = baseIdentifier[..baseIdentifier.LastIndexOf('_')];
    string bitness = baseIdentifier[(baseIdentifier.LastIndexOf('_') + 1)..];
    binaryType = bitness switch { "32" => BinaryType.BIT32, "64" => BinaryType.BIT64, _ => BinaryType.Unknown };
}

系统默认提供20种不同类型的代理DLL,覆盖从图形接口(dxgi.dll)到输入设备(dinput8.dll)等多个系统组件,确保对不同游戏引擎的广泛兼容性。

2.2 平台特定解锁模块

CreamInstaller为不同游戏平台提供专用解锁模块,各模块针对平台特性采用不同的技术实现:

  • SmokeAPI(Steam平台):通过替换steam_api.dll实现对Steamworks API的拦截,修改DLC授权检查逻辑。配置文件采用JSON格式,支持DLC状态覆盖和额外DLC注入。

  • ScreamAPI(Epic平台):针对Epic Online Services (EOS) SDK设计,通过代理EOSSDK-Win32-Shipping.dll和EOSSDK-Win64-Shipping.dll实现DLC权限绕过。

  • UplayR1/R2(Ubisoft平台):针对Ubisoft Connect不同版本设计的解锁方案,通过替换uplay_r1_loader.dll和upc_r2_loader.dll实现授权验证绕过。

各模块均实现统一的Install/Uninstall接口,确保操作一致性:

// SmokeAPI安装实现示例
internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true)
{
    // 重命名原始Steamworks DLL
    if (File.Exists(api32) && !File.Exists(api32_o))
    {
        File.Move(api32, api32_o!);
        installForm?.UpdateUser($"Renamed Steamworks: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false);
    }
    // 写入代理DLL
    if (File.Exists(api32_o))
    {
        "SmokeAPI.steam_api.dll".Write(api32);
        installForm?.UpdateUser($"Wrote SmokeAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false);
    }
    // 生成配置文件
    if (generateConfig)
        CheckConfig(directory, selection, installForm);
}

3. 游戏识别与扫描机制

CreamInstaller采用多层次扫描策略实现游戏检测,确保准确识别已安装游戏及其DLC信息。

3.1 Steam平台游戏扫描

Steam平台扫描通过解析Steam安装目录下的libraryfolders.vdf文件获取游戏库位置,然后遍历每个库目录中的appmanifest_.acf文件提取游戏信息:

// 解析Steam库目录
private static async Task<List<string>> GetLibraryDirectories()
{
    List<string> gameDirectories = new();
    string steamInstallPath = InstallPath;
    if (steamInstallPath == null || !Directory.Exists(steamInstallPath))
        return gameDirectories;
    string libraryFolder = steamInstallPath + @"\steamapps";
    if (!Directory.Exists(libraryFolder))
        return gameDirectories;
    gameDirectories.Add(libraryFolder);
    // 解析额外库目录
    string libraryFolders = libraryFolder + @"\libraryfolders.vdf";
    if (File.Exists(libraryFolders) && ValveDataFile.TryDeserialize(File.ReadAllText(libraryFolders, Encoding.UTF8), out VProperty result))
    {
        foreach (VToken vToken in result.Value.Where(p => p is VProperty property && int.TryParse(property.Key, out int _)))
        {
            VProperty property = (VProperty)vToken;
            string path = property.Value.GetChild("path")?.ToString();
            if (!string.IsNullOrWhiteSpace(path))
            {
                path += @"\steamapps";
                if (Directory.Exists(path) && !gameDirectories.Contains(path))
                    gameDirectories.Add(path);
            }
        }
    }
    return gameDirectories;
}

3.2 游戏可执行文件检测

系统通过分析游戏目录中的可执行文件确定游戏的架构类型(32位/64位),为后续组件安装提供依据:

// 检测目录中可执行文件的二进制类型
foreach (string executable in Directory.EnumerateFiles(directory, "*.exe"))
    if (executable.TryGetFileBinaryType(out BinaryType binaryType))
    {
        switch (binaryType)
        {
            case BinaryType.BIT32:
                bit32 = true;
                break;
            case BinaryType.BIT64:
                bit64 = true;
                break;
        }
        if (bit32 && bit64)
            break;
    }

4. 配置管理系统

CreamInstaller采用JSON格式的配置文件实现解锁参数的持久化存储,配置生成逻辑根据不同平台特性动态调整:

4.1 SmokeAPI配置生成

SmokeAPI配置文件(SmokeAPI.config.json)包含DLC状态覆盖、额外DLC注入等信息:

private static void WriteConfig(StreamWriter writer, string appId,
    SortedList<string, (string name, SortedList<string, (DlcType type, string name, string icon)> injectDlc)> extraApps,
    SortedList<string, (DlcType type, string name, string icon)> overrideDlc, 
    SortedList<string, (DlcType type, string name, string icon)> injectDlc,
    InstallForm installForm = null)
{
    writer.WriteLine("{");
    writer.WriteLine("  \"$version\": 2,");
    writer.WriteLine("  \"logging\": false,");
    writer.WriteLine("  \"unlock_family_sharing\": true,");
    writer.WriteLine("  \"default_app_status\": \"unlocked\",");
    writer.WriteLine("  \"override_app_status\": {},");
    // 写入DLC覆盖配置
    if (overrideDlc.Count > 0)
    {
        writer.WriteLine("  \"override_dlc_status\": {");
        KeyValuePair<string, (DlcType type, string name, string icon)> lastOverrideDlc = overrideDlc.Last();
        foreach (KeyValuePair<string, (DlcType type, string name, string icon)> pair in overrideDlc)
        {
            string dlcId = pair.Key;
            (_, string dlcName, _) = pair.Value;
            writer.WriteLine($"    \"{dlcId}\": \"locked\"{(pair.Equals(lastOverrideDlc) ? "" : ",")}");
            installForm?.UpdateUser($"Added locked DLC to SmokeAPI.config.json with appid {dlcId} ({dlcName})", LogTextBox.Action, false);
        }
        writer.WriteLine("  },");
    }
    // 其他配置项...
    writer.WriteLine("}");
}

4.2 配置版本迁移

系统支持配置文件的版本迁移,确保向下兼容性:

// 处理旧版配置文件
if (File.Exists(old_config))
{
    if (!File.Exists(config))
    {
        File.Move(old_config, config!);
        installForm?.UpdateUser($"Converted old configuration: {Path.GetFileName(old_config)} -> {Path.GetFileName(config)}", LogTextBox.Action, false);
    }
    else
    {
        File.Delete(old_config);
        installForm?.UpdateUser($"Deleted old configuration: {Path.GetFileName(old_config)}", LogTextBox.Action, false);
    }
}

5. 安装流程与事务管理

CreamInstaller的安装过程采用事务式设计,确保操作的原子性和可恢复性:

5.1 安装流程控制

InstallForm类实现了完整的安装流程控制逻辑,通过状态机管理安装过程:

private async Task Operate()
{
    List<ProgramSelection> programSelections = ProgramSelection.AllEnabled;
    operationsCount = programSelections.Count;
    completeOperationsCount = 0;
    foreach (ProgramSelection selection in programSelections)
    {
        if (Program.Canceled || !Program.IsProgramRunningDialog(this, selection))
            throw new CustomMessageException("The operation was canceled.");
        try
        {
            await OperateFor(selection);
            UpdateUser($"Operation succeeded for {selection.Name}.", LogTextBox.Success);
            selection.Enabled = false;
            disabledSelections.Add(selection);
        }
        catch (Exception exception)
        {
            UpdateUser($"Operation failed for {selection.Name}: " + exception, LogTextBox.Error);
        }
        ++completeOperationsCount;
    }
    // 后续处理...
}

5.2 错误处理与恢复

系统实现了完善的错误处理机制,通过ExceptionHandler类统一处理操作过程中的异常:

internal static bool HandleException(this Exception e, Form form = null, string caption = null, 
    string acceptButtonText = "Retry", string cancelButtonText = "Cancel")
{
    caption ??= Program.Name + " encountered an exception";
    StringBuilder output = new();
    int stackDepth = 0;
    while (e is not null && stackDepth <= 10)
    {
        // 构建异常信息...
        e = e.InnerException;
        stackDepth++;
    }
    using DialogForm dialogForm = new(form ?? Form.ActiveForm);
    return dialogForm.Show(SystemIcons.Error, output.ToString(), acceptButtonText, cancelButtonText, caption) == DialogResult.OK;
}

6. 性能优化策略

CreamInstaller在设计中采用多项性能优化策略,确保在大型游戏库环境下的高效运行:

6.1 异步扫描与并行处理

游戏扫描过程采用异步编程模型,避免UI线程阻塞:

internal static async Task<List<(string appId, string name, string branch, int buildId, string gameDirectory)>> GetGames()
=> await Task.Run(async () =>
{
    List<(string appId, string name, string branch, int buildId, string gameDirectory)> games = new();
    List<string> gameLibraryDirectories = await GetLibraryDirectories();
    foreach (string libraryDirectory in gameLibraryDirectories)
    {
        if (Program.Canceled)
            return games;
        foreach ((string appId, string name, string branch, int buildId, string gameDirectory) game in
                 (await GetGamesFromLibraryDirectory(libraryDirectory)).Where(game
                     => !games.Any(_game => _game.appId == game.appId && _game.gameDirectory == game.gameDirectory)))
            games.Add(game);
    }
    return games;
});

6.2 资源管理与内存优化

系统通过嵌入式资源管理DLL文件,避免文件系统依赖:

// 从嵌入式资源写入代理DLL
private static void WriteProxy(this string path, string proxyName, BinaryType binaryType)
{
    foreach (string resourceIdentifier in EmbeddedResources.FindAll(r => r.StartsWith("Koaloader")))
    {
        resourceIdentifier.GetProxyInfoFromIdentifier(out string _proxyName, out BinaryType _binaryType);
        if (_proxyName != proxyName || _binaryType != binaryType)
            continue;
        resourceIdentifier.Write(path);
        break;
    }
}

7. 安全与风险控制

7.1 操作安全性保障

系统实现多项安全机制确保操作安全:

  • 文件操作前的存在性检查
  • 关键操作的用户确认
  • 操作过程的日志记录
  • 异常情况下的回滚机制

7.2 潜在风险与缓解策略

使用CreamInstaller可能面临的技术风险及应对策略:

  1. 游戏更新导致解锁失效

    • 缓解:定期更新解锁组件,监控游戏平台API变化
  2. 系统稳定性风险

    • 缓解:采用DLL代理而非直接修改游戏文件,便于恢复
  3. 账号安全风险

    • 缓解:避免在在线游戏中使用,防止账号处罚

8. 部署与环境要求

8.1 系统要求

  • 操作系统:Windows 7或更高版本(32位/64位)
  • .NET运行时:.NET 7.0 Runtime或更高版本
  • 硬盘空间:至少100MB可用空间
  • 管理员权限:部分操作需要管理员权限

8.2 编译与部署

从源码构建CreamInstaller的步骤:

# 克隆代码仓库
git clone https://gitcode.com/gh_mirrors/cr/CreamApi
cd CreamApi

# 编译项目
dotnet build CreamInstaller.sln -c Release

编译生成的可执行文件位于CreamInstaller/bin/Release/net7.0-windows/目录下。

9. 故障排查与诊断

9.1 常见问题诊断

系统提供多种诊断工具帮助排查问题:

// 诊断工具示例
internal static class Diagnostics
{
    internal static void OpenFileInNotepad(string path)
    {
        string npp = NotepadPlusPlusPath + @"\notepad++.exe";
        if (File.Exists(npp))
            OpenFileInNotepadPlusPlus(npp, path);
        else
            OpenFileInWindowsNotepad(path);
    }
    
    internal static void OpenDirectoryInFileExplorer(string path) 
        => Process.Start(new ProcessStartInfo { FileName = "explorer.exe", Arguments = path });
}

9.2 日志系统

操作过程中的详细日志通过LogTextBox组件记录,可用于问题诊断:

internal void UpdateUser(string text, Color color, bool info = true, bool log = true)
{
    if (info)
        _ = userInfoLabel.Invoke(() => userInfoLabel.Text = text);
    if (log && !logTextBox.Disposing && !logTextBox.IsDisposed)
        logTextBox.Invoke(() =>
        {
            if (logTextBox.Text.Length > 0)
                logTextBox.AppendText(Environment.NewLine, color);
            logTextBox.AppendText(text, color);
            logTextBox.Invalidate();
        });
}

10. 技术发展展望

CreamInstaller作为一款开源工具,未来发展将聚焦于以下几个方向:

  1. 多平台支持扩展:增加对GOG、Origin等更多游戏平台的支持
  2. 插件系统:引入插件架构,允许社区开发自定义解锁方案
  3. 性能优化:进一步优化大型游戏库的扫描性能
  4. 用户体验改进:增强自动化配置能力,减少手动干预

通过持续的技术创新和社区贡献,CreamInstaller有望成为游戏DLC管理领域的标准工具之一。

登录后查看全文
热门项目推荐
相关项目推荐