Rework read/write zip file loading/saving.
This commit is contained in:
@@ -53,9 +53,14 @@ namespace OpenRA.FileSystem
|
|||||||
|
|
||||||
public IReadOnlyPackage OpenPackage(string filename, FileSystem context)
|
public IReadOnlyPackage OpenPackage(string filename, FileSystem context)
|
||||||
{
|
{
|
||||||
var subFolder = Platform.ResolvePath(Path.Combine(Name, filename));
|
var resolvedPath = Platform.ResolvePath(Path.Combine(Name, filename));
|
||||||
if (Directory.Exists(subFolder))
|
if (Directory.Exists(resolvedPath))
|
||||||
return new Folder(subFolder);
|
return new Folder(resolvedPath);
|
||||||
|
|
||||||
|
// Zip files loaded from Folders (and *only* from Folders) can be read-write
|
||||||
|
IReadWritePackage readWritePackage;
|
||||||
|
if (ZipFileLoader.TryParseReadWritePackage(resolvedPath, out readWritePackage))
|
||||||
|
return readWritePackage;
|
||||||
|
|
||||||
// Other package types can be loaded normally
|
// Other package types can be loaded normally
|
||||||
IReadOnlyPackage package;
|
IReadOnlyPackage package;
|
||||||
|
|||||||
@@ -23,38 +23,23 @@ namespace OpenRA.FileSystem
|
|||||||
{
|
{
|
||||||
static readonly string[] Extensions = { ".zip", ".oramap" };
|
static readonly string[] Extensions = { ".zip", ".oramap" };
|
||||||
|
|
||||||
sealed class ZipFile : IReadWritePackage
|
class ReadOnlyZipFile : IReadOnlyPackage
|
||||||
{
|
{
|
||||||
public IReadWritePackage Parent { get; private set; }
|
public string Name { get; protected set; }
|
||||||
public string Name { get; private set; }
|
protected SZipFile pkg;
|
||||||
readonly Stream pkgStream;
|
|
||||||
readonly SZipFile pkg;
|
|
||||||
|
|
||||||
static ZipFile()
|
static ReadOnlyZipFile()
|
||||||
{
|
{
|
||||||
ZipConstants.DefaultCodePage = Encoding.UTF8.CodePage;
|
ZipConstants.DefaultCodePage = Encoding.UTF8.CodePage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZipFile(Stream stream, string name, IReadOnlyPackage parent = null)
|
// Dummy constructor for use with ReadWriteZipFile
|
||||||
|
protected ReadOnlyZipFile() { }
|
||||||
|
|
||||||
|
public ReadOnlyZipFile(Stream s, string 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;
|
|
||||||
|
|
||||||
Name = name;
|
|
||||||
Parent = parent as IReadWritePackage;
|
|
||||||
pkg = new SZipFile(pkgStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ZipFile(string filename, IReadWritePackage parent)
|
|
||||||
{
|
|
||||||
pkgStream = new MemoryStream();
|
|
||||||
|
|
||||||
Name = filename;
|
Name = filename;
|
||||||
Parent = parent;
|
pkg = new SZipFile(s);
|
||||||
pkg = SZipFile.Create(pkgStream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream GetStream(string filename)
|
public Stream GetStream(string filename)
|
||||||
@@ -86,14 +71,61 @@ namespace OpenRA.FileSystem
|
|||||||
return pkg.GetEntry(filename) != null;
|
return pkg.GetEntry(filename) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (pkg != null)
|
||||||
|
pkg.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyPackage OpenPackage(string filename, FileSystem context)
|
||||||
|
{
|
||||||
|
// Directories are stored with a trailing "/" in the index
|
||||||
|
var entry = pkg.GetEntry(filename) ?? pkg.GetEntry(filename + "/");
|
||||||
|
if (entry == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (entry.IsDirectory)
|
||||||
|
return new ZipFolder(this, filename);
|
||||||
|
|
||||||
|
// Other package types can be loaded normally
|
||||||
|
IReadOnlyPackage package;
|
||||||
|
var s = GetStream(filename);
|
||||||
|
if (s == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (context.TryParsePackage(s, filename, out package))
|
||||||
|
return package;
|
||||||
|
|
||||||
|
s.Dispose();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class ReadWriteZipFile : ReadOnlyZipFile, IReadWritePackage
|
||||||
|
{
|
||||||
|
readonly MemoryStream pkgStream;
|
||||||
|
|
||||||
|
public ReadWriteZipFile(Stream stream, string 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();
|
||||||
|
if (stream != null)
|
||||||
|
{
|
||||||
|
stream.CopyTo(pkgStream);
|
||||||
|
stream.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgStream.Position = 0;
|
||||||
|
pkg = new SZipFile(pkgStream);
|
||||||
|
Name = filename;
|
||||||
|
}
|
||||||
|
|
||||||
void Commit()
|
void Commit()
|
||||||
{
|
{
|
||||||
if (Parent == null)
|
|
||||||
throw new InvalidDataException("Cannot update ZipFile without writable parent");
|
|
||||||
|
|
||||||
var pos = pkgStream.Position;
|
var pos = pkgStream.Position;
|
||||||
pkgStream.Position = 0;
|
pkgStream.Position = 0;
|
||||||
Parent.Update(Name, pkgStream.ReadBytes((int)pkgStream.Length));
|
File.WriteAllBytes(Name, pkgStream.ReadBytes((int)pkgStream.Length));
|
||||||
pkgStream.Position = pos;
|
pkgStream.Position = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,47 +144,12 @@ namespace OpenRA.FileSystem
|
|||||||
pkg.CommitUpdate();
|
pkg.CommitUpdate();
|
||||||
Commit();
|
Commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (pkg != null)
|
|
||||||
pkg.Close();
|
|
||||||
|
|
||||||
if (pkgStream != null)
|
|
||||||
pkgStream.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IReadOnlyPackage OpenPackage(string filename, FileSystem context)
|
|
||||||
{
|
|
||||||
// Directories are stored with a trailing "/" in the index
|
|
||||||
var entry = pkg.GetEntry(filename) ?? pkg.GetEntry(filename + "/");
|
|
||||||
if (entry == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (entry.IsDirectory)
|
|
||||||
return new ZipFolder(this, filename);
|
|
||||||
|
|
||||||
if (Extensions.Any(e => filename.EndsWith(e, StringComparison.InvariantCultureIgnoreCase)))
|
|
||||||
return new ZipFile(GetStream(filename), filename, this);
|
|
||||||
|
|
||||||
// Other package types can be loaded normally
|
|
||||||
IReadOnlyPackage package;
|
|
||||||
var s = GetStream(filename);
|
|
||||||
if (s == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (context.TryParsePackage(s, filename, out package))
|
|
||||||
return package;
|
|
||||||
|
|
||||||
s.Dispose();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class ZipFolder : IReadOnlyPackage
|
sealed class ZipFolder : IReadOnlyPackage
|
||||||
{
|
{
|
||||||
public string Name { get { return path; } }
|
public string Name { get { return path; } }
|
||||||
public ZipFile Parent { get; private set; }
|
public ReadOnlyZipFile Parent { get; private set; }
|
||||||
readonly string path;
|
readonly string path;
|
||||||
|
|
||||||
static ZipFolder()
|
static ZipFolder()
|
||||||
@@ -160,7 +157,7 @@ namespace OpenRA.FileSystem
|
|||||||
ZipConstants.DefaultCodePage = Encoding.UTF8.CodePage;
|
ZipConstants.DefaultCodePage = Encoding.UTF8.CodePage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZipFolder(ZipFile parent, string path)
|
public ZipFolder(ReadOnlyZipFile parent, string path)
|
||||||
{
|
{
|
||||||
if (path.EndsWith("/", StringComparison.Ordinal))
|
if (path.EndsWith("/", StringComparison.Ordinal))
|
||||||
path = path.Substring(0, path.Length - 1);
|
path = path.Substring(0, path.Length - 1);
|
||||||
@@ -227,19 +224,26 @@ namespace OpenRA.FileSystem
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
string name;
|
package = new ReadOnlyZipFile(s, filename);
|
||||||
IReadOnlyPackage p;
|
|
||||||
if (context.TryGetPackageContaining(filename, out p, out name))
|
|
||||||
package = new ZipFile(p.GetStream(name), name, p);
|
|
||||||
else
|
|
||||||
package = new ZipFile(s, filename, null);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IReadWritePackage Create(string filename, IReadWritePackage parent)
|
public static bool TryParseReadWritePackage(string filename, out IReadWritePackage package)
|
||||||
{
|
{
|
||||||
return new ZipFile(filename, parent);
|
if (!Extensions.Any(e => filename.EndsWith(e, StringComparison.InvariantCultureIgnoreCase)))
|
||||||
|
{
|
||||||
|
package = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var s = new FileStream(filename, FileMode.Open);
|
||||||
|
package = new ReadWriteZipFile(s, filename);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IReadWritePackage Create(string filename)
|
||||||
|
{
|
||||||
|
return new ReadWriteZipFile(null, filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
|||||||
map.PlayerDefinitions = mapPlayers.ToMiniYaml();
|
map.PlayerDefinitions = mapPlayers.ToMiniYaml();
|
||||||
|
|
||||||
var dest = Path.GetFileNameWithoutExtension(args[1]) + ".oramap";
|
var dest = Path.GetFileNameWithoutExtension(args[1]) + ".oramap";
|
||||||
map.Save(ZipFileLoader.Create(dest, new Folder(".")));
|
map.Save(ZipFileLoader.Create(dest));
|
||||||
Console.WriteLine(dest + " saved.");
|
Console.WriteLine(dest + " saved.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
|||||||
|
|
||||||
var dest = Path.GetFileNameWithoutExtension(args[1]) + ".oramap";
|
var dest = Path.GetFileNameWithoutExtension(args[1]) + ".oramap";
|
||||||
|
|
||||||
Map.Save(ZipFileLoader.Create(dest, new Folder(".")));
|
Map.Save(ZipFileLoader.Create(dest));
|
||||||
Console.WriteLine(dest + " saved.");
|
Console.WriteLine(dest + " saved.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
{
|
{
|
||||||
selectedDirectory.Folder.Delete(combinedPath);
|
selectedDirectory.Folder.Delete(combinedPath);
|
||||||
if (fileType == MapFileType.OraMap)
|
if (fileType == MapFileType.OraMap)
|
||||||
package = ZipFileLoader.Create(combinedPath, selectedDirectory.Folder);
|
package = ZipFileLoader.Create(combinedPath);
|
||||||
else
|
else
|
||||||
package = new Folder(combinedPath);
|
package = new Folder(combinedPath);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ namespace OpenRA.Mods.D2k.UtilityCommands
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var dest = Path.GetFileNameWithoutExtension(args[1]) + ".oramap";
|
var dest = Path.GetFileNameWithoutExtension(args[1]) + ".oramap";
|
||||||
map.Save(ZipFileLoader.Create(dest, new Folder(".")));
|
map.Save(ZipFileLoader.Create(dest));
|
||||||
Console.WriteLine(dest + " saved.");
|
Console.WriteLine(dest + " saved.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user