CamelCase for FileSystem folder

This commit is contained in:
Matthias Mailänder
2013-10-12 11:00:37 +02:00
parent 32851e624c
commit 0b73491e3f
7 changed files with 6 additions and 6 deletions

View File

@@ -0,0 +1,102 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
namespace OpenRA.FileFormats
{
public class D2kSoundResources : IFolder
{
readonly Stream s;
readonly string filename;
readonly List<string> filenames;
readonly int priority;
readonly Dictionary<uint, PackageEntry> index = new Dictionary<uint, PackageEntry>();
public D2kSoundResources(string filename, int priority)
{
this.filename = filename;
this.priority = priority;
s = FileSystem.Open(filename);
s.Seek(0, SeekOrigin.Begin);
filenames = new List<string>();
var headerLength = s.ReadUInt32();
while (s.Position < headerLength + 4)
{
var name = s.ReadASCIIZ();
var offset = s.ReadUInt32();
var length = s.ReadUInt32();
var hash = PackageEntry.HashFilename(name, PackageHashType.Classic);
if (!index.ContainsKey(hash))
index.Add(hash, new PackageEntry(hash, offset, length));
filenames.Add(name);
}
}
public Stream GetContent(uint hash)
{
PackageEntry e;
if (!index.TryGetValue(hash, out e))
return null;
s.Seek(e.Offset, SeekOrigin.Begin);
var data = new byte[e.Length];
s.Read(data, 0, (int)e.Length);
return new MemoryStream(data);
}
public Stream GetContent(string filename)
{
return GetContent(PackageEntry.HashFilename(filename, PackageHashType.Classic));
}
public bool Exists(string filename)
{
return index.ContainsKey(PackageEntry.HashFilename(filename, PackageHashType.Classic));
}
public IEnumerable<string> AllFileNames()
{
return filenames;
}
public string Name { get { return filename; } }
public int Priority { get { return 1000 + priority; }}
public IEnumerable<uint> ClassicHashes()
{
return index.Keys;
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public void Write(Dictionary<string, byte[]> contents)
{
throw new NotImplementedException("Cannot save Dune 2000 Sound Resources.");
}
}
}

View File

@@ -0,0 +1,212 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace OpenRA.FileFormats
{
public static class FileSystem
{
public static List<IFolder> MountedFolders = new List<IFolder>();
static Cache<uint, List<IFolder>> classicHashIndex = new Cache<uint, List<IFolder>>( _ => new List<IFolder>() );
static Cache<uint, List<IFolder>> crcHashIndex = new Cache<uint, List<IFolder>>( _ => new List<IFolder>() );
public static List<string> FolderPaths = new List<string>();
static void MountInner(IFolder folder)
{
MountedFolders.Add(folder);
foreach (var hash in folder.ClassicHashes())
{
var l = classicHashIndex[hash];
if (!l.Contains(folder))
l.Add(folder);
}
foreach (var hash in folder.CrcHashes())
{
var l = crcHashIndex[hash];
if (!l.Contains(folder))
l.Add(folder);
}
}
static int order = 0;
public static IFolder CreatePackage(string filename, int order, Dictionary<string, byte[]> content)
{
if (filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase))
return new MixFile(filename, order, content);
else if (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(filename, order, content);
else if (filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(filename, order, content);
else if (filename.EndsWith(".RS", StringComparison.InvariantCultureIgnoreCase))
throw new NotImplementedException("Creating .RS archives is unsupported");
else if (filename.EndsWith(".Z", StringComparison.InvariantCultureIgnoreCase))
throw new NotImplementedException("Creating .Z archives is unsupported");
else
return new Folder(filename, order, content);
}
public static IFolder OpenPackage(string filename, string annotation, int order)
{
if (filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase))
{
var type = string.IsNullOrEmpty(annotation) ? PackageHashType.Classic :
FieldLoader.GetValue<PackageHashType>("(value)", annotation);
return new MixFile(filename, type, order);
}
else if (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(filename, order);
else if (filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(filename, order);
else if (filename.EndsWith(".RS", StringComparison.InvariantCultureIgnoreCase))
return new D2kSoundResources(filename, order);
else if (filename.EndsWith(".Z", StringComparison.InvariantCultureIgnoreCase))
return new InstallShieldPackage(filename, order);
else
return new Folder(filename, order);
}
public static void Mount(string name)
{
Mount(name, null);
}
public static void Mount(string name, string annotation)
{
var optional = name.StartsWith("~");
if (optional) name = name.Substring(1);
// paths starting with ^ are relative to the support dir
if (name.StartsWith("^"))
name = Platform.SupportDir+name.Substring(1);
FolderPaths.Add(name);
Action a = () => FileSystem.MountInner(OpenPackage(name, annotation, order++));
if (optional)
try { a(); }
catch { }
else
a();
}
public static void UnmountAll()
{
MountedFolders.Clear();
FolderPaths.Clear();
classicHashIndex = new Cache<uint, List<IFolder>>(_ => new List<IFolder>());
crcHashIndex = new Cache<uint, List<IFolder>>(_ => new List<IFolder>());
}
public static bool Unmount(IFolder mount)
{
return (MountedFolders.RemoveAll(f => f == mount) > 0);
}
public static void Mount(IFolder mount)
{
if (!MountedFolders.Contains(mount)) MountedFolders.Add(mount);
}
public static void LoadFromManifest(Manifest manifest)
{
UnmountAll();
foreach (var dir in manifest.Folders)
Mount(dir);
foreach (var pkg in manifest.Packages)
Mount(pkg.Key, pkg.Value);
}
static Stream GetFromCache(PackageHashType type, string filename)
{
var index = type == PackageHashType.CRC32 ? crcHashIndex : classicHashIndex;
var folder = index[PackageEntry.HashFilename(filename, type)]
.Where(x => x.Exists(filename))
.OrderBy(x => x.Priority)
.FirstOrDefault();
if (folder != null)
return folder.GetContent(filename);
return null;
}
public static Stream Open(string filename) { return OpenWithExts(filename, ""); }
public static Stream OpenWithExts(string filename, params string[] exts)
{
if (filename.IndexOfAny(new char[] { '/', '\\' } ) == -1)
{
foreach (var ext in exts)
{
var s = GetFromCache(PackageHashType.Classic, filename + ext);
if (s != null)
return s;
s = GetFromCache(PackageHashType.CRC32, filename + ext);
if (s != null)
return s;
}
}
foreach (var ext in exts)
{
var folder = MountedFolders
.Where(x => x.Exists(filename + ext))
.OrderByDescending(x => x.Priority)
.FirstOrDefault();
if (folder != null)
return folder.GetContent(filename + ext);
}
throw new FileNotFoundException("File not found: {0}".F(filename), filename);
}
public static bool Exists(string filename) { return MountedFolders.Any(f => f.Exists(filename)); }
static Dictionary<string, Assembly> assemblyCache = new Dictionary<string, Assembly>();
public static Assembly ResolveAssembly(object sender, ResolveEventArgs e)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
if (assembly.FullName == e.Name)
return assembly;
var frags = e.Name.Split(',');
var filename = frags[0] + ".dll";
Assembly a;
if (assemblyCache.TryGetValue(filename, out a))
return a;
if (FileSystem.Exists(filename))
using (var s = FileSystem.Open(filename))
{
var buf = new byte[s.Length];
s.Read(buf, 0, buf.Length);
a = Assembly.Load(buf);
assemblyCache.Add(filename, a);
return a;
}
return null;
}
}
}

View File

@@ -0,0 +1,83 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.IO;
using System;
namespace OpenRA.FileFormats
{
public class Folder : IFolder
{
readonly string path;
readonly int priority;
// Create a new folder package
public Folder(string path, int priority, Dictionary<string, byte[]> contents)
{
this.path = path;
this.priority = priority;
if (Directory.Exists(path))
Directory.Delete(path, true);
Write(contents);
}
public Folder(string path, int priority)
{
this.path = path;
this.priority = priority;
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
}
public Stream GetContent(string filename)
{
try { return File.OpenRead( Path.Combine( path, filename ) ); }
catch { return null; }
}
public IEnumerable<uint> ClassicHashes()
{
foreach (var filename in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly))
yield return PackageEntry.HashFilename(Path.GetFileName(filename), PackageHashType.Classic);
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames()
{
foreach (var filename in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly))
yield return Path.GetFileName(filename);
}
public bool Exists(string filename)
{
return File.Exists(Path.Combine(path, filename));
}
public int Priority { get { return priority; } }
public string Name { get { return path; } }
public void Write(Dictionary<string, byte[]> contents)
{
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
foreach (var file in contents)
using (var dataStream = File.Create(Path.Combine(path, file.Key)))
using (var writer = new BinaryWriter(dataStream))
writer.Write(file.Value);
}
}
}

View File

@@ -0,0 +1,145 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace OpenRA.FileFormats
{
public class InstallShieldPackage : IFolder
{
readonly Dictionary<uint, PackageEntry> index = new Dictionary<uint, PackageEntry>();
readonly List<string> filenames;
readonly Stream s;
readonly long dataStart = 255;
readonly int priority;
readonly string filename;
public InstallShieldPackage(string filename, int priority)
{
this.filename = filename;
this.priority = priority;
filenames = new List<string>();
s = FileSystem.Open(filename);
// Parse package header
BinaryReader reader = new BinaryReader(s);
uint signature = reader.ReadUInt32();
if (signature != 0x8C655D13)
throw new InvalidDataException("Not an Installshield package");
reader.ReadBytes(8);
/*var FileCount = */reader.ReadUInt16();
reader.ReadBytes(4);
/*var ArchiveSize = */reader.ReadUInt32();
reader.ReadBytes(19);
var TOCAddress = reader.ReadInt32();
reader.ReadBytes(4);
var DirCount = reader.ReadUInt16();
// Parse the directory list
s.Seek(TOCAddress, SeekOrigin.Begin);
BinaryReader TOCreader = new BinaryReader(s);
var fileCountInDirs = new List<uint>();
// Parse directories
for (var i = 0; i < DirCount; i++)
fileCountInDirs.Add(ParseDirectory(TOCreader));
// Parse files
foreach (var fileCount in fileCountInDirs)
for (var i = 0; i < fileCount; i++)
ParseFile(reader);
}
uint ParseDirectory(BinaryReader reader)
{
// Parse directory header
var FileCount = reader.ReadUInt16();
var ChunkSize = reader.ReadUInt16();
var NameLength = reader.ReadUInt16();
reader.ReadChars(NameLength); //var DirName = new String(reader.ReadChars(NameLength));
// Skip to the end of the chunk
reader.ReadBytes(ChunkSize - NameLength - 6);
return FileCount;
}
uint AccumulatedData = 0;
void ParseFile(BinaryReader reader)
{
reader.ReadBytes(7);
var CompressedSize = reader.ReadUInt32();
reader.ReadBytes(12);
var ChunkSize = reader.ReadUInt16();
reader.ReadBytes(4);
var NameLength = reader.ReadByte();
var FileName = new String(reader.ReadChars(NameLength));
var hash = PackageEntry.HashFilename(FileName, PackageHashType.Classic);
if (!index.ContainsKey(hash))
index.Add(hash, new PackageEntry(hash,AccumulatedData, CompressedSize));
filenames.Add(FileName);
AccumulatedData += CompressedSize;
// Skip to the end of the chunk
reader.ReadBytes(ChunkSize - NameLength - 30);
}
public Stream GetContent(uint hash)
{
PackageEntry e;
if (!index.TryGetValue(hash, out e))
return null;
s.Seek(dataStart + e.Offset, SeekOrigin.Begin);
var data = new byte[e.Length];
s.Read(data, 0, (int)e.Length);
return new MemoryStream(Blast.Decompress(data));
}
public Stream GetContent(string filename)
{
return GetContent(PackageEntry.HashFilename(filename, PackageHashType.Classic));
}
public IEnumerable<uint> ClassicHashes()
{
return index.Keys;
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames()
{
return filenames;
}
public bool Exists(string filename)
{
return index.ContainsKey(PackageEntry.HashFilename(filename, PackageHashType.Classic));
}
public int Priority { get { return 2000 + priority; }}
public string Name { get { return filename; } }
public void Write(Dictionary<string, byte[]> contents)
{
throw new NotImplementedException("Cannot save InstallShieldPackages.");
}
}
}

View File

@@ -0,0 +1,274 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
namespace OpenRA.FileFormats
{
public interface IFolder
{
Stream GetContent(string filename);
bool Exists(string filename);
IEnumerable<uint> ClassicHashes();
IEnumerable<uint> CrcHashes();
IEnumerable<string> AllFileNames();
void Write(Dictionary<string, byte[]> contents);
int Priority { get; }
string Name { get; }
}
public class MixFile : IFolder
{
readonly Dictionary<uint, PackageEntry> index;
readonly long dataStart;
readonly Stream s;
readonly int priority;
readonly string filename;
readonly PackageHashType type;
// Save a mix to disk with the given contents
public MixFile(string filename, int priority, Dictionary<string, byte[]> contents)
{
this.filename = filename;
this.priority = priority;
this.type = PackageHashType.Classic;
if (File.Exists(filename))
File.Delete(filename);
s = File.Create(filename);
index = new Dictionary<uint, PackageEntry>();
contents.Add("local mix database.dat", new XccLocalDatabase(contents.Keys.Append("local mix database.dat")).Data());
Write(contents);
}
public MixFile(string filename, PackageHashType type, int priority)
{
this.filename = filename;
this.priority = priority;
this.type = type;
s = FileSystem.Open(filename);
// Detect format type
s.Seek(0, SeekOrigin.Begin);
var isCncMix = s.ReadUInt16() != 0;
// The C&C mix format doesn't contain any flags or encryption
var isEncrypted = false;
if (!isCncMix)
isEncrypted = (s.ReadUInt16() & 0x2) != 0;
List<PackageEntry> entries;
if (isEncrypted)
{
long unused;
entries = ParseHeader(DecryptHeader(s, 4, out dataStart), 0, out unused);
}
else
entries = ParseHeader(s, isCncMix ? 0 : 4, out dataStart);
index = entries.ToDictionaryWithConflictLog(x => x.Hash,
"{0} ({1} format, Encrypted: {2}, DataStart: {3})".F(filename, (isCncMix ? "C&C" : "RA/TS/RA2"), isEncrypted, dataStart),
null, x => "(offs={0}, len={1})".F(x.Offset, x.Length)
);
}
List<PackageEntry> ParseHeader(Stream s, long offset, out long headerEnd)
{
s.Seek(offset, SeekOrigin.Begin);
var numFiles = s.ReadUInt16();
/*uint dataSize = */s.ReadUInt32();
var items = new List<PackageEntry>();
for (var i = 0; i < numFiles; i++)
items.Add(new PackageEntry(s));
headerEnd = offset + 6 + numFiles*PackageEntry.Size;
return items;
}
MemoryStream DecryptHeader(Stream s, long offset, out long headerEnd)
{
s.Seek(offset, SeekOrigin.Begin);
// Decrypt blowfish key
var keyblock = s.ReadBytes(80);
var blowfishKey = new BlowfishKeyProvider().DecryptKey(keyblock);
var fish = new Blowfish(blowfishKey);
// Decrypt first block to work out the header length
var ms = Decrypt(ReadBlocks(s, offset + 80, 1), fish);
var numFiles = ms.ReadUInt16();
// Decrypt the full header - round bytes up to a full block
var blockCount = (13 + numFiles*PackageEntry.Size)/8;
headerEnd = offset + 80 + blockCount*8;
return Decrypt(ReadBlocks(s, offset + 80, blockCount), fish);
}
static MemoryStream Decrypt(uint[] h, Blowfish fish)
{
var decrypted = fish.Decrypt(h);
var ms = new MemoryStream();
var writer = new BinaryWriter(ms);
foreach(var t in decrypted)
writer.Write(t);
writer.Flush();
ms.Seek(0, SeekOrigin.Begin);
return ms;
}
uint[] ReadBlocks(Stream s, long offset, int count)
{
s.Seek(offset, SeekOrigin.Begin);
// A block is a single encryption unit (represented as two 32-bit integers)
var ret = new uint[2*count];
for (var i = 0; i < ret.Length; i++)
ret[i] = s.ReadUInt32();
return ret;
}
uint? FindMatchingHash(string filename)
{
var hash = PackageEntry.HashFilename(filename, type);
if (index.ContainsKey(hash))
return hash;
// Maybe we were given a raw hash?
uint raw;
if (!uint.TryParse(filename, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out raw))
return null;
if ("{0:X}".F(raw) == filename && index.ContainsKey(raw))
return raw;
return null;
}
public Stream GetContent(uint hash)
{
PackageEntry e;
if (!index.TryGetValue(hash, out e))
return null;
s.Seek(dataStart + e.Offset, SeekOrigin.Begin);
var data = new byte[e.Length];
s.Read(data, 0, (int)e.Length);
return new MemoryStream(data);
}
public Stream GetContent(string filename)
{
var hash = FindMatchingHash(filename);
return hash.HasValue ? GetContent(hash.Value) : null;
}
static readonly uint[] Nothing = {};
public IEnumerable<uint> ClassicHashes()
{
if (type == PackageHashType.Classic)
return index.Keys;
return Nothing;
}
public IEnumerable<uint> CrcHashes()
{
if (type == PackageHashType.CRC32)
return index.Keys;
return Nothing;
}
public IEnumerable<string> AllFileNames()
{
var lookup = new Dictionary<uint, string>();
if (Exists("local mix database.dat"))
{
var db = new XccLocalDatabase(GetContent("local mix database.dat"));
foreach (var e in db.Entries)
{
var hash = PackageEntry.HashFilename(e, type);
if (!lookup.ContainsKey(hash))
lookup.Add(hash, e);
}
}
if (FileSystem.Exists("global mix database.dat"))
{
var db = new XccGlobalDatabase(FileSystem.Open("global mix database.dat"));
foreach (var e in db.Entries)
{
var hash = PackageEntry.HashFilename(e, type);
if (!lookup.ContainsKey(hash))
lookup.Add(hash, e);
}
}
return index.Keys.Select(k => lookup.ContainsKey(k) ? lookup[k] : "{0:X}".F(k));
}
public bool Exists(string filename)
{
return FindMatchingHash(filename).HasValue;
}
public int Priority { get { return 1000 + priority; } }
public string Name { get { return filename; } }
public void Write(Dictionary<string, byte[]> contents)
{
// Cannot modify existing mixfile - rename existing file and
// create a new one with original content plus modifications
FileSystem.Unmount(this);
// TODO: Add existing data to the contents list
if (index.Count > 0)
throw new NotImplementedException("Updating mix files unfinished");
// Construct a list of entries for the file header
uint dataSize = 0;
var items = new List<PackageEntry>();
foreach (var kv in contents)
{
var length = (uint)kv.Value.Length;
var hash = PackageEntry.HashFilename(Path.GetFileName(kv.Key), type);
items.Add(new PackageEntry(hash, dataSize, length));
dataSize += length;
}
// Write the new file
s.Seek(0, SeekOrigin.Begin);
using (var writer = new BinaryWriter(s))
{
// Write file header
writer.Write((ushort)items.Count);
writer.Write(dataSize);
foreach (var item in items)
item.Write(writer);
writer.Flush();
// Copy file data
foreach (var file in contents)
s.Write(file.Value);
}
}
}
}

View File

@@ -0,0 +1,121 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
using SZipFile = ICSharpCode.SharpZipLib.Zip.ZipFile;
namespace OpenRA.FileFormats
{
public class ZipFile : IFolder
{
string filename;
SZipFile pkg;
int priority;
public ZipFile(string filename, int priority)
{
this.filename = filename;
this.priority = priority;
try
{
// pull the file into memory, dont keep it open.
pkg = new SZipFile(new MemoryStream(File.ReadAllBytes(filename)));
}
catch (ZipException e)
{
Log.Write("debug", "Couldn't load zip file: {0}", e.Message);
}
}
// Create a new zip with the specified contents
public ZipFile(string filename, int priority, Dictionary<string, byte[]> contents)
{
this.priority = priority;
this.filename = filename;
if (File.Exists(filename))
File.Delete(filename);
pkg = SZipFile.Create(filename);
Write(contents);
}
public Stream GetContent(string filename)
{
using (var z = pkg.GetInputStream(pkg.GetEntry(filename)))
{
var ms = new MemoryStream();
int bufSize = 2048;
byte[] buf = new byte[bufSize];
while ((bufSize = z.Read(buf, 0, buf.Length)) > 0)
ms.Write(buf, 0, bufSize);
ms.Seek(0, SeekOrigin.Begin);
return ms;
}
}
public IEnumerable<uint> ClassicHashes()
{
foreach (ZipEntry entry in pkg)
yield return PackageEntry.HashFilename(entry.Name, PackageHashType.Classic);
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames()
{
foreach (ZipEntry entry in pkg)
yield return entry.Name;
}
public bool Exists(string filename)
{
return pkg.GetEntry(filename) != null;
}
public int Priority { get { return 500 + priority; } }
public string Name { get { return filename; } }
public void Write(Dictionary<string, byte[]> contents)
{
// TODO: Clear existing content?
pkg.Close();
pkg = SZipFile.Create(filename);
pkg.BeginUpdate();
foreach (var kvp in contents)
pkg.Add(new StaticMemoryDataSource(kvp.Value), kvp.Key);
pkg.CommitUpdate();
pkg.Close();
pkg = new SZipFile(new MemoryStream(File.ReadAllBytes(filename)));
}
}
class StaticMemoryDataSource : IStaticDataSource
{
byte[] data;
public StaticMemoryDataSource(byte[] data)
{
this.data = data;
}
public Stream GetSource()
{
return new MemoryStream(data);
}
}
}