Created
June 9, 2015 13:18
-
-
Save penev92/f19f0e875648d887a8f4 to your computer and use it in GitHub Desktop.
This file contains 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
#region Copyright & License Information | |
/* | |
* Copyright 2007-2015 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 ICSharpCode.SharpZipLib.Zip; | |
using ICSharpCode.SharpZipLib.Zip.Compression; | |
using ICSharpCode.SharpZipLib.Zip.Compression.Streams; | |
namespace OpenRA.FileSystem | |
{ | |
// Useful literature: | |
// https://msdn.microsoft.com/en-us/library/bb417343.aspx#cabinet_format | |
// http://download.microsoft.com/download/5/0/1/501ED102-E53F-4CE0-AA6B-B0F93629DDC6/Exchange/%5BMS-CAB%5D.pdf | |
// https://msdn.microsoft.com/en-us/library/cc483131%28v=exchg.80%29.aspx | |
public sealed class MicrosoftCABFile : IFolder, IDisposable | |
{ | |
struct CFHeader | |
{ | |
public uint cbCabinet; /* size of this cabinet file in bytes */ | |
public uint coffFiles; /* absolute offset of the first CFFILE entry */ | |
public byte versionMinor; /* cabinet file format version, minor */ | |
public byte versionMajor; /* cabinet file format version, major */ | |
public ushort cFolders; /* number of CFFOLDER entries in this cabinet */ | |
public ushort cFiles; /* number of CFFILE entries in this cabinet */ | |
public ushort flags; /* cabinet file option indicators */ | |
public ushort setID; /* must be the same for all cabinets in a set */ | |
public ushort iCabinet; /* number of this cabinet file in a set */ | |
public ushort cbCFHeader; /* (optional) size of per-cabinet reserved area */ | |
public byte cbCFFolder; /* (optional) size of per-folder reserved area */ | |
public byte cbCFData; /* (optional) size of per-datablock reserved area */ | |
public byte[] abReserve; /* (optional) per-cabinet reserved area */ | |
public byte[] szCabinetPrev; /* (optional) name of previous cabinet file */ | |
public byte[] szDiskPrev; /* (optional) name of previous disk */ | |
public byte[] szCabinetNext; /* (optional) name of next cabinet file */ | |
public byte[] szDiskNext; /* (optional) name of next disk */ | |
public CFHeader(BinaryReader reader) | |
{ | |
cbCFHeader = 0; | |
cbCFFolder = 0; | |
cbCFData = 0; | |
szCabinetPrev = szDiskPrev = szCabinetNext = szDiskNext = new byte[0]; | |
// Reserved field, set to zero. | |
reader.ReadInt32(); | |
cbCabinet = reader.ReadUInt32(); | |
// Reserved field, set to zero. | |
reader.ReadInt32(); | |
coffFiles = reader.ReadUInt32(); | |
// Reserved field, set to zero. | |
reader.ReadInt32(); | |
versionMinor = reader.ReadByte(); | |
versionMajor = reader.ReadByte(); | |
cFolders = reader.ReadUInt16(); | |
cFiles = reader.ReadUInt16(); | |
flags = reader.ReadUInt16(); | |
var hasPreviousCabinet = (flags & 1) != 0; | |
var hasNextCabinet = (flags & 2) != 0; | |
var hasReserved = (flags & 4) != 0; | |
setID = reader.ReadUInt16(); | |
iCabinet = reader.ReadUInt16(); | |
if (hasReserved) | |
{ | |
cbCFHeader = reader.ReadUInt16(); | |
cbCFFolder = reader.ReadByte(); | |
cbCFData = reader.ReadByte(); | |
} | |
abReserve = reader.ReadBytes(cbCFHeader); | |
if (hasPreviousCabinet) | |
{ | |
szCabinetPrev = reader.ReadBytes(255); | |
szDiskPrev = reader.ReadBytes(255); | |
} | |
if (hasNextCabinet) | |
{ | |
szCabinetNext = reader.ReadBytes(255); | |
szDiskNext = reader.ReadBytes(255); | |
} | |
} | |
} | |
struct CFFolder | |
{ | |
public uint coffCabStart; /* absolute offset of the first CFDATA block in this folder */ | |
public ushort cCFData; /* number of CFDATA blocks in this folder */ | |
ushort typeCompress; /* compression type indicator */ | |
byte[] abReserve; /* (optional) per-folder reserved area */ | |
public CFFolder(BinaryReader reader, byte cbCFFolder) | |
{ | |
coffCabStart = reader.ReadUInt32(); | |
cCFData = reader.ReadUInt16(); | |
typeCompress = reader.ReadUInt16(); | |
abReserve = reader.ReadBytes(cbCFFolder); | |
} | |
} | |
struct CFFile | |
{ | |
public string FileName; | |
public uint cbFile; /* uncompressed size of this file in bytes */ | |
public uint uoffFolderStart; /* uncompressed offset of this file in the folder */ | |
public ushort iFolder; /* index into the CFFOLDER area */ | |
ushort date; /* date stamp for this file */ | |
ushort time; /* time stamp for this file */ | |
ushort attribs; /* attribute flags for this file */ | |
public CFFile(BinaryReader reader) | |
{ | |
cbFile = reader.ReadUInt32(); | |
uoffFolderStart = reader.ReadUInt32(); | |
iFolder = reader.ReadUInt16(); | |
date = reader.ReadUInt16(); | |
time = reader.ReadUInt16(); | |
attribs = reader.ReadUInt16(); | |
var name = new List<byte>(); | |
while (true) | |
{ | |
var current = reader.ReadByte(); | |
if (current == '\0') | |
break; | |
name.Add(current); | |
} | |
FileName = System.Text.Encoding.UTF8.GetString(name.ToArray()); | |
} | |
} | |
struct CFData | |
{ | |
uint csum; /* checksum of this CFDATA entry */ | |
ushort cbData; /* number of compressed bytes in this block */ | |
public ushort cbUncomp; /* number of uncompressed bytes in this block */ | |
byte[] abReserve; /* (optional) per-datablock reserved area */ | |
public byte[] Data; /* compressed data bytes */ | |
public CFData(BinaryReader reader, ushort cbCFHeader, byte cbCFData) | |
{ | |
csum = reader.ReadUInt32(); | |
cbData = reader.ReadUInt16(); | |
cbUncomp = reader.ReadUInt16(); | |
abReserve = reader.ReadBytes(cbCFData); | |
// A 0x43, 0x4B header | |
var signature = reader.ReadChars(2); | |
if (string.Concat(signature) != "CK") | |
csum = 0; | |
Data = reader.ReadBytes(cbData-2); | |
} | |
} | |
string filename; | |
public MicrosoftCABFile(string filename) | |
{ | |
using (var stream = GlobalFileSystem.Open(filename)) | |
using (var reader = new BinaryReader(stream)) | |
{ | |
var signature = reader.ReadChars(4); | |
if (string.Concat(signature) != "MSCF") | |
throw new InvalidDataException("Not a Microsoft CAB package!"); | |
var header = new CFHeader(reader); | |
var cfFolders = new List<CFFolder>(); | |
for (var i = 0; i < header.cFolders; i++) | |
cfFolders.Add(new CFFolder(reader, header.cbCFFolder)); | |
var cfFiles = new List<CFFile>(); | |
stream.Seek(header.coffFiles, SeekOrigin.Begin); | |
for (var i = 0; i < header.cFiles; i++) | |
cfFiles.Add(new CFFile(reader)); | |
var folderNumber = 0; | |
foreach (var folder in cfFolders) | |
{ | |
stream.Seek(folder.coffCabStart, SeekOrigin.Begin); | |
var cfData = new List<CFData>(); | |
for (var i = 0; i < folder.cCFData; i++) | |
{ | |
var cfdata = new CFData(reader, header.cbCFHeader, header.cbCFData); | |
cfData.Add(cfdata); | |
} | |
var fileName = cfFiles.First(x => x.iFolder == folderNumber).FileName; | |
//var inf = new List<byte>(); | |
using (var inflaterStream = new InflaterStream(cfData)) | |
using (var outputStream = File.OpenWrite(fileName)) | |
using (var originalStream = File.OpenRead(@"\ra2_original\language.mix")) | |
{ | |
var buffer = new byte[32768]; | |
while (inflaterStream.CanRead) | |
{ | |
var output = inflaterStream.Read(buffer, 0, 0); | |
//inf.AddRange(buffer.Take(output)); | |
var write = buffer.Take(output).ToArray(); | |
//var compare = originalStream.ReadBytes(output); | |
//for (var i = 0; i < output; i++) | |
// if (write[i] != compare[i]) | |
// Console.WriteLine("asdf"); | |
outputStream.Write(write, 0, output); | |
outputStream.Flush(); | |
} | |
} | |
folderNumber++; | |
} | |
} | |
} | |
public static byte[] FlateDecode(byte[] inp, bool strict) | |
{ | |
byte[] inflatedData; | |
using (var stream = new MemoryStream(inp)) | |
using (var inflater = new InflaterInputStream(stream, new Inflater(true))) | |
using (var outputStream = new MemoryStream()) | |
{ | |
var b = new byte[strict ? 4092 : 1]; | |
try | |
{ | |
int n; | |
while ((n = inflater.Read(b, 0, b.Length)) > 0) | |
outputStream.Write(b, 0, n); | |
inflatedData = outputStream.ToArray(); | |
inflater.Close(); | |
outputStream.Close(); | |
} | |
catch | |
{ | |
inflatedData = new byte[0]; | |
if (!strict) | |
inflatedData = outputStream.ToArray(); | |
inflater.Close(); | |
outputStream.Close(); | |
} | |
} | |
return inflatedData; | |
} | |
public Stream GetContent(string filename) | |
{ | |
return null; | |
} | |
public IEnumerable<uint> ClassicHashes() | |
{ | |
yield break; | |
} | |
public IEnumerable<uint> CrcHashes() | |
{ | |
yield break; | |
} | |
public IEnumerable<string> AllFileNames() | |
{ | |
yield break; | |
} | |
public bool Exists(string filename) | |
{ | |
return false; | |
} | |
public int Priority { get { return 500 + 0; } } | |
public string Name { get { return filename; } } | |
public void Write(Dictionary<string, byte[]> contents) | |
{ | |
throw new NotImplementedException("Cannot save CAB archives."); | |
} | |
public void Dispose() | |
{ | |
} | |
class InflaterStream : Stream | |
{ | |
Queue<CFData> data; | |
public InflaterStream(IEnumerable<CFData> data) | |
{ | |
this.data = new Queue<CFData>(data); | |
} | |
public override void Flush() | |
{ | |
throw new NotImplementedException(); | |
} | |
public override long Seek(long offset, SeekOrigin origin) | |
{ | |
throw new NotImplementedException(); | |
} | |
public override void SetLength(long value) | |
{ | |
throw new NotImplementedException(); | |
} | |
public override int Read(byte[] buffer, int offset, int count) | |
{ | |
var d = data.Dequeue(); | |
var tmp = FlateDecode(d.Data, false); | |
Array.Copy(tmp, buffer, d.cbUncomp); | |
return d.cbUncomp; | |
} | |
public override void Write(byte[] buffer, int offset, int count) | |
{ | |
throw new NotImplementedException(); | |
} | |
public override bool CanRead | |
{ | |
get { return data.Count > 0; } | |
} | |
public override bool CanSeek | |
{ | |
get { throw new NotImplementedException(); } | |
} | |
public override bool CanWrite | |
{ | |
get { throw new NotImplementedException(); } | |
} | |
public override long Length | |
{ | |
get { return data.Count; } | |
} | |
public override long Position { get; set; } | |
public static byte[] FlateDecode(byte[] inp, bool strict) | |
{ | |
byte[] inflatedData; | |
using (var stream = new MemoryStream(inp)) | |
using (var inflater = new InflaterInputStream(stream, new Inflater(true))) | |
using (var outputStream = new MemoryStream()) | |
{ | |
var b = new byte[strict ? 4092 : 1]; | |
try | |
{ | |
int n; | |
while ((n = inflater.Read(b, 0, b.Length)) > 0) | |
outputStream.Write(b, 0, n); | |
inflatedData = outputStream.ToArray(); | |
inflater.Close(); | |
outputStream.Close(); | |
} | |
catch | |
{ | |
inflatedData = new byte[0]; | |
if (!strict) | |
inflatedData = outputStream.ToArray(); | |
inflater.Close(); | |
outputStream.Close(); | |
} | |
} | |
return inflatedData; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment