MsDelta.cs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. using System.ComponentModel;
  2. namespace MeloongCore;
  3. /// <summary>
  4. /// 使用 Windows MsDelta API 生成、应用增量补丁文件。
  5. /// </summary>
  6. public static class MsDelta {
  7. /// <summary>
  8. /// 生成增量补丁文件:比较 <paramref name="oldFilePath"/> 和 <paramref name="newFilePath"/>,将补丁写入 <paramref name="deltaFilePath"/>。
  9. /// </summary>
  10. /// <exception cref="FileNotFoundException" />
  11. /// <exception cref="Win32Exception" />
  12. public static void Create(string oldFilePath, string newFilePath, string deltaFilePath) {
  13. if (!FileUtils.Exists(oldFilePath)) throw new FileNotFoundException($"旧文件不存在:{oldFilePath}", oldFilePath);
  14. if (!FileUtils.Exists(newFilePath)) throw new FileNotFoundException($"新文件不存在:{newFilePath}", newFilePath);
  15. DirectoryUtils.Create(PathUtils.RemoveLastPart(deltaFilePath));
  16. Logger.Info($"正在生成补丁:{oldFilePath} → {newFilePath},补丁文件将输出到:{deltaFilePath}");
  17. // fileTypeSet:https://learn.microsoft.com/en-us/previous-versions/bb417345(v=msdn.10)?redirectedfrom=MSDN#file-type-sets (15L: DELTA_FILE_TYPE_SET_EXECUTABLES)
  18. // 131072L: IgnoreFileSizeLimit, 32u: Crc32
  19. if (!CreateDelta(15L, 131072L, 0L,
  20. PathUtils.ForApi(oldFilePath), PathUtils.ForApi(newFilePath), null, null, new DeltaInput(), IntPtr.Zero, 32u, PathUtils.ForApi(deltaFilePath)))
  21. throw new Win32Exception();
  22. }
  23. /// <summary>
  24. /// 应用增量补丁文件:将 <paramref name="deltaFilePath"/> 补丁应用到 <paramref name="oldFilePath"/>,将结果写入 <paramref name="newFilePath"/>。
  25. /// </summary>
  26. /// <exception cref="FileNotFoundException" />
  27. /// <exception cref="Win32Exception" />
  28. public static void Apply(string oldFilePath, string deltaFilePath, string newFilePath) {
  29. if (!FileUtils.Exists(oldFilePath)) throw new FileNotFoundException($"旧文件不存在:{oldFilePath}", oldFilePath);
  30. if (!FileUtils.Exists(deltaFilePath)) throw new FileNotFoundException($"补丁文件不存在:{deltaFilePath}", deltaFilePath);
  31. DirectoryUtils.Create(PathUtils.RemoveLastPart(newFilePath));
  32. Logger.Info($"正在应用补丁:原始文件 {oldFilePath} + 补丁文件 {deltaFilePath} → 输出至 {newFilePath}");
  33. // 1L: AllowLegacy
  34. if (!ApplyDelta(1L, PathUtils.ForApi(oldFilePath), PathUtils.ForApi(deltaFilePath), PathUtils.ForApi(newFilePath)))
  35. throw new Win32Exception();
  36. }
  37. // API
  38. [StructLayout(LayoutKind.Sequential)]
  39. private struct DeltaInput {
  40. public IntPtr Start;
  41. public IntPtr Size;
  42. [MarshalAs(UnmanagedType.Bool)] public bool Editable;
  43. }
  44. [DllImport("msdelta.dll", CharSet = CharSet.Unicode, SetLastError = true)]
  45. [return: MarshalAs(UnmanagedType.Bool)]
  46. private static extern bool CreateDelta(
  47. long fileTypeSet, long setFlags, long resetFlags,
  48. string sourceName, string targetName, string? sourceOptionsName,
  49. string? targetOptionsName, DeltaInput globalOptions,
  50. IntPtr targetFileTime, uint hashAlgId, string deltaName);
  51. [DllImport("msdelta.dll", CharSet = CharSet.Unicode, SetLastError = true)]
  52. [return: MarshalAs(UnmanagedType.Bool)]
  53. private static extern bool ApplyDelta(long applyFlags, string sourceName, string deltaName, string targetName);
  54. }