Created
October 23, 2011 04:52
-
-
Save azyobuzin/1306890 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Runtime.Serialization.Json; | |
using System.Text; | |
using System.Xml; | |
using System.Xml.Linq; | |
using Ionic.Zip; | |
using Ionic.Zlib; | |
namespace Updater | |
{ | |
public static class UpdaterCore | |
{ | |
public static void ApplyPatch(string patchFile, string targetDir) | |
{ | |
using (var zip = ZipFile.Read(patchFile)) | |
{ | |
foreach (var entry in zip) | |
{ | |
using (var entryStream = new MemoryStream()) | |
{ | |
entry.Extract(entryStream); | |
entryStream.Position = 0; | |
var targetFileName = Path.Combine(targetDir, entry.FileName); | |
switch (entryStream.ReadByte()) | |
{ | |
case 0://パッチ | |
XElement elm; | |
using (var reader = JsonReaderWriterFactory.CreateJsonReader(entryStream, XmlDictionaryReaderQuotas.Max)) | |
elm = XElement.Load(reader); | |
var @base = | |
Convert.ToBase64String( | |
File.ReadAllBytes(targetFileName), | |
Base64FormattingOptions.InsertLineBreaks | |
) | |
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) | |
.ToList(); | |
foreach (var diff in elm.Elements()) | |
{ | |
@base[(int)diff.Element("index")] = (string)diff.Element("data"); | |
} | |
File.WriteAllBytes(targetFileName, Convert.FromBase64String(string.Join("\r\n", @base))); | |
break; | |
case 1://バイナリ | |
using (var fs = new FileStream(targetFileName, FileMode.Create, FileAccess.Write)) | |
{ | |
while (true) | |
{ | |
var bs = new byte[1024 * 4]; | |
var readCount = entryStream.Read(bs, 0, bs.Length); | |
if (readCount == 0) | |
break; | |
fs.Write(bs, 0, readCount); | |
} | |
} | |
break; | |
case 2://削除 | |
File.Delete(targetFileName); | |
break; | |
default: | |
throw new ArgumentException("パッチが正しくありません。"); | |
} | |
} | |
} | |
} | |
} | |
public static void CreatePatch(string oldZipFile, string newZipFile, string outputFile) | |
{ | |
using (var zip = new ZipFile()) | |
using (var oldZip = ZipFile.Read(oldZipFile)) | |
using (var newZip = ZipFile.Read(newZipFile)) | |
{ | |
zip.CompressionLevel = CompressionLevel.BestCompression; | |
foreach (var newEntry in newZip.Except(oldZip, ZipEntryFileNameComparer.Default)) | |
{ | |
var memStream = new MemoryStream(); | |
memStream.WriteByte(1); | |
newEntry.Extract(memStream); | |
zip.AddEntry(newEntry.FileName, memStream); | |
} | |
foreach (var deleteEntry in oldZip.Except(newZip, ZipEntryFileNameComparer.Default)) | |
{ | |
zip.AddEntry(deleteEntry.FileName, new byte[] { 2 }); | |
} | |
foreach (var otherEntry in | |
oldZip.Where(entry => newZip.Contains(entry, ZipEntryFileNameComparer.Default)) | |
.Zip(newZip.Where(entry => oldZip.Contains(entry, ZipEntryFileNameComparer.Default)), | |
(f, s) => new { old = f, @new = s })) | |
{ | |
var oldBase64 = Convert.ToBase64String(Extract(otherEntry.old), Base64FormattingOptions.InsertLineBreaks) | |
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); | |
var newBase64 = Convert.ToBase64String(Extract(otherEntry.@new), Base64FormattingOptions.InsertLineBreaks) | |
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); | |
var diff = new List<Tuple<int, string>>(); | |
if (oldBase64.Length < newBase64.Length) | |
{ | |
diff.AddRange( | |
newBase64.Select((data, index) => Tuple.Create(index, data)) | |
.Skip(oldBase64.Length) | |
); | |
} | |
if (oldBase64.Length > newBase64.Length) | |
{ | |
diff.AddRange( | |
oldBase64.Select((data, index) => Tuple.Create(index, string.Empty)) | |
.Skip(newBase64.Length) | |
); | |
} | |
var minLength = Math.Min(oldBase64.Length, newBase64.Length); | |
diff.AddRange( | |
oldBase64.Take(minLength) | |
.Select((data, index) => Tuple.Create(index, data)) | |
.Zip(newBase64.Take(minLength), | |
(f, s) => new { index = f.Item1, old = f.Item2, @new = s }) | |
.Where(_ => _.old != _.@new) | |
.Select(_ => Tuple.Create(_.index, _.@new)) | |
); | |
if (diff.Any()) | |
{ | |
diff.Sort((f, s) => f.Item1.CompareTo(s.Item1)); | |
var memStream = new MemoryStream(); | |
memStream.WriteByte(0); | |
using (var writer = JsonReaderWriterFactory.CreateJsonWriter(memStream, Encoding.UTF8, false)) | |
{ | |
var elm = new XElement("root", new XAttribute("type", "array")); | |
elm.Add(diff.Select(d => | |
new XElement( | |
"item", | |
new XAttribute("type", "object"), | |
new XElement("index", d.Item1), | |
new XElement("data", d.Item2) | |
) | |
)); | |
elm.Save(writer); | |
} | |
zip.AddEntry([email protected], memStream); | |
} | |
} | |
zip.Save(outputFile); | |
} | |
} | |
private static byte[] Extract(ZipEntry entry) | |
{ | |
using (var memStream = new MemoryStream()) | |
{ | |
entry.Extract(memStream); | |
return memStream.ToArray(); | |
} | |
} | |
} | |
class ZipEntryFileNameComparer : IEqualityComparer<ZipEntry> | |
{ | |
private static ZipEntryFileNameComparer instance; | |
public static ZipEntryFileNameComparer Default | |
{ | |
get | |
{ | |
return instance = instance ?? new ZipEntryFileNameComparer(); | |
} | |
} | |
public bool Equals(ZipEntry x, ZipEntry y) | |
{ | |
return x.FileName == y.FileName; | |
} | |
public int GetHashCode(ZipEntry obj) | |
{ | |
return obj.FileName.GetHashCode(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment