Merge pull request #11482 from pchote/rework-zip-updating

Rework zip updating
This commit is contained in:
reaperrr
2016-07-02 17:00:06 +02:00
committed by GitHub
9 changed files with 104 additions and 63 deletions

View File

@@ -71,26 +71,13 @@ namespace OpenRA.FileSystem
public IReadOnlyPackage OpenPackage(string filename, IReadOnlyPackage parent) public IReadOnlyPackage OpenPackage(string filename, IReadOnlyPackage parent)
{ {
// HACK: limit support to zip and folder until we generalize the PackageLoader support // HACK: limit support to zip and folder until we generalize the PackageLoader support
if (parent is Folder) if (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase) ||
filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
{ {
var path = Path.Combine(parent.Name, filename); using (var s = parent.GetStream(filename))
return new ZipFile(s, filename, parent);
// HACK: work around SharpZipLib's lack of support for writing to in-memory files
if (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(this, path);
if (filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(this, path);
var subFolder = Platform.ResolvePath(path);
if (Directory.Exists(subFolder))
return new Folder(subFolder);
} }
if (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(this, filename, parent.GetStream(filename));
if (filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(this, filename, parent.GetStream(filename));
if (parent is ZipFile) if (parent is ZipFile)
return new ZipFolder(this, (ZipFile)parent, filename, filename); return new ZipFolder(this, (ZipFile)parent, filename, filename);
@@ -100,17 +87,14 @@ namespace OpenRA.FileSystem
return new ZipFolder(this, folder.Parent, folder.Name + "/" + filename, filename); return new ZipFolder(this, folder.Parent, folder.Name + "/" + filename, filename);
} }
return null; if (parent is Folder)
{
var subFolder = Platform.ResolvePath(Path.Combine(parent.Name, filename));
if (Directory.Exists(subFolder))
return new Folder(subFolder);
} }
public IReadWritePackage OpenWritablePackage(string filename) return null;
{
if (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(this, filename);
if (filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(this, filename);
return new Folder(filename);
} }
public void Mount(string name, string explicitName = null) public void Mount(string name, string explicitName = null)

View File

@@ -52,10 +52,15 @@ namespace OpenRA.FileSystem
public void Update(string filename, byte[] contents) public void Update(string filename, byte[] contents)
{ {
if (!Directory.Exists(path)) // HACK: ZipFiles can't be loaded as read-write from a stream, so we are
Directory.CreateDirectory(path); // forced to bypass the parent package and load them with their full path
// in FileSystem.OpenPackage. Their internal name therefore contains the
// full parent path too. We need to be careful to not add a second path
// prefix to these hacked packages.
var filePath = filename.StartsWith(path) ? filename : Path.Combine(path, filename);
using (var s = File.Create(Path.Combine(path, filename))) Directory.CreateDirectory(Path.GetDirectoryName(filePath));
using (var s = File.Create(filePath))
s.Write(contents, 0, contents.Length); s.Write(contents, 0, contents.Length);
} }

View File

@@ -9,8 +9,10 @@
*/ */
#endregion #endregion
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using ICSharpCode.SharpZipLib.Zip; using ICSharpCode.SharpZipLib.Zip;
using SZipFile = ICSharpCode.SharpZipLib.Zip.ZipFile; using SZipFile = ICSharpCode.SharpZipLib.Zip.ZipFile;
@@ -19,32 +21,60 @@ namespace OpenRA.FileSystem
{ {
public sealed class ZipFile : IReadWritePackage public sealed class ZipFile : IReadWritePackage
{ {
public IReadWritePackage Parent { get; private set; }
public string Name { get; private set; } public string Name { get; private set; }
SZipFile pkg; readonly Stream pkgStream;
readonly SZipFile pkg;
static ZipFile() static ZipFile()
{ {
ZipConstants.DefaultCodePage = Encoding.UTF8.CodePage; ZipConstants.DefaultCodePage = Encoding.UTF8.CodePage;
} }
public ZipFile(FileSystem context, string filename, Stream stream, bool createOrClearContents = false) public ZipFile(Stream stream, string name, IReadOnlyPackage parent = null)
{ {
Name = filename; // SharpZipLib breaks when asked to update archives loaded from outside streams or files
// We can work around this by creating a clean in-memory-only file, cutting all outside references
pkgStream = new MemoryStream();
stream.CopyTo(pkgStream);
pkgStream.Position = 0;
if (createOrClearContents) Name = name;
pkg = SZipFile.Create(stream); Parent = parent as IReadWritePackage;
else pkg = new SZipFile(pkgStream);
pkg = new SZipFile(stream);
} }
public ZipFile(IReadOnlyFileSystem context, string filename, bool createOrClearContents = false) public ZipFile(IReadOnlyFileSystem context, string filename)
{ {
Name = filename; string name;
IReadOnlyPackage p;
if (!context.TryGetPackageContaining(filename, out p, out name))
throw new FileNotFoundException("Unable to find parent package for " + filename);
if (createOrClearContents) Name = name;
pkg = SZipFile.Create(filename); Parent = p as IReadWritePackage;
else
pkg = new SZipFile(filename); // SharpZipLib breaks when asked to update archives loaded from outside streams or files
// We can work around this by creating a clean in-memory-only file, cutting all outside references
pkgStream = new MemoryStream();
p.GetStream(name).CopyTo(pkgStream);
pkgStream.Position = 0;
pkg = new SZipFile(pkgStream);
}
ZipFile(string filename, IReadWritePackage parent)
{
pkgStream = new MemoryStream();
Name = filename;
Parent = parent;
pkg = SZipFile.Create(pkgStream);
}
public static ZipFile Create(string filename, IReadWritePackage parent)
{
return new ZipFile(filename, parent);
} }
public Stream GetStream(string filename) public Stream GetStream(string filename)
@@ -76,11 +106,23 @@ namespace OpenRA.FileSystem
return pkg.GetEntry(filename) != null; return pkg.GetEntry(filename) != null;
} }
void Commit()
{
if (Parent == null)
throw new InvalidDataException("Cannot update ZipFile without writable parent");
var pos = pkgStream.Position;
pkgStream.Position = 0;
Parent.Update(Name, pkgStream.ReadBytes((int)pkgStream.Length));
pkgStream.Position = pos;
}
public void Update(string filename, byte[] contents) public void Update(string filename, byte[] contents)
{ {
pkg.BeginUpdate(); pkg.BeginUpdate();
pkg.Add(new StaticMemoryDataSource(contents), filename); pkg.Add(new StaticStreamDataSource(new MemoryStream(contents)), filename);
pkg.CommitUpdate(); pkg.CommitUpdate();
Commit();
} }
public void Delete(string filename) public void Delete(string filename)
@@ -88,6 +130,7 @@ namespace OpenRA.FileSystem
pkg.BeginUpdate(); pkg.BeginUpdate();
pkg.Delete(filename); pkg.Delete(filename);
pkg.CommitUpdate(); pkg.CommitUpdate();
Commit();
} }
public void Dispose() public void Dispose()
@@ -97,17 +140,17 @@ namespace OpenRA.FileSystem
} }
} }
class StaticMemoryDataSource : IStaticDataSource class StaticStreamDataSource : IStaticDataSource
{ {
byte[] data; readonly Stream s;
public StaticMemoryDataSource(byte[] data) public StaticStreamDataSource(Stream s)
{ {
this.data = data; this.s = s;
} }
public Stream GetSource() public Stream GetSource()
{ {
return new MemoryStream(data); return s;
} }
} }
} }

View File

@@ -47,7 +47,7 @@ namespace OpenRA
{ {
try try
{ {
package = new ZipFile(null, pair.Second); package = new ZipFile(File.OpenRead(pair.Second), pair.Second);
} }
catch catch
{ {

View File

@@ -105,9 +105,8 @@ namespace OpenRA.Mods.Common.UtilityCommands
Map.FixOpenAreas(); Map.FixOpenAreas();
var dest = Path.GetFileNameWithoutExtension(args[1]) + ".oramap"; var dest = Path.GetFileNameWithoutExtension(args[1]) + ".oramap";
var package = new ZipFile(modData.ModFiles, dest, true);
Map.Save(package); Map.Save(ZipFile.Create(dest, new Folder(".")));
Console.WriteLine(dest + " saved."); Console.WriteLine(dest + " saved.");
} }

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Text; using System.Text;
using OpenRA.FileSystem; using OpenRA.FileSystem;
@@ -37,9 +38,16 @@ namespace OpenRA.Mods.Common.UtilityCommands
var files = FieldLoader.GetValue<string[]>("value", yaml.Value); var files = FieldLoader.GetValue<string[]>("value", yaml.Value);
foreach (var filename in files) foreach (var filename in files)
{ {
var fileNodes = MiniYaml.FromStream(map.Package.GetStream(filename), filename); var fileNodes = MiniYaml.FromStream(map.Open(filename), filename);
processYaml(modData, engineDate, ref fileNodes, null, 0); processYaml(modData, engineDate, ref fileNodes, null, 0);
((IReadWritePackage)map.Package).Update(filename, Encoding.ASCII.GetBytes(fileNodes.WriteToString()));
// HACK: Obtain the writable save path using knowledge of the underlying filesystem workings
var packagePath = filename;
var package = map.Package;
if (filename.Contains("|"))
modData.DefaultFileSystem.TryGetPackageContaining(filename, out package, out packagePath);
((IReadWritePackage)package).Update(packagePath, Encoding.ASCII.GetBytes(fileNodes.WriteToString()));
} }
} }
@@ -71,7 +79,11 @@ namespace OpenRA.Mods.Common.UtilityCommands
// HACK: The engine code assumes that Game.modData is set. // HACK: The engine code assumes that Game.modData is set.
Game.ModData = modData; Game.ModData = modData;
var package = modData.ModFiles.OpenWritablePackage(args[1]); // HACK: We know that maps can only be oramap or folders, which are ReadWrite
var package = modData.ModFiles.OpenPackage(args[1], new Folder(".")) as IReadWritePackage;
if (package == null)
throw new FileNotFoundException(args[1]);
var engineDate = Exts.ParseIntegerInvariant(args[2]); var engineDate = Exts.ParseIntegerInvariant(args[2]);
UpgradeMap(modData, package, engineDate); UpgradeMap(modData, package, engineDate);
} }

View File

@@ -180,7 +180,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
selectedDirectory.Folder.Delete(combinedPath); selectedDirectory.Folder.Delete(combinedPath);
if (fileType == MapFileType.OraMap) if (fileType == MapFileType.OraMap)
package = new ZipFile(modData.DefaultFileSystem, combinedPath, true); package = ZipFile.Create(combinedPath, selectedDirectory.Folder);
else else
package = new Folder(combinedPath); package = new Folder(combinedPath);
} }

View File

@@ -37,8 +37,7 @@ namespace OpenRA.Mods.D2k.UtilityCommands
return; return;
var dest = Path.GetFileNameWithoutExtension(args[1]) + ".oramap"; var dest = Path.GetFileNameWithoutExtension(args[1]) + ".oramap";
var package = new ZipFile(modData.DefaultFileSystem, dest, true); map.Save(ZipFile.Create(dest, new Folder(".")));
map.Save(package);
Console.WriteLine(dest + " saved."); Console.WriteLine(dest + " saved.");
} }
} }

View File

@@ -186,8 +186,7 @@ namespace OpenRA.Mods.TS.UtilityCommands
map.PlayerDefinitions = mapPlayers.ToMiniYaml(); map.PlayerDefinitions = mapPlayers.ToMiniYaml();
var dest = Path.GetFileNameWithoutExtension(args[1]) + ".oramap"; var dest = Path.GetFileNameWithoutExtension(args[1]) + ".oramap";
var package = new ZipFile(modData.DefaultFileSystem, dest, true); map.Save(ZipFile.Create(dest, new Folder(".")));
map.Save(package);
Console.WriteLine(dest + " saved."); Console.WriteLine(dest + " saved.");
} }