Files
SabreTools/SabreTools.Library/External/Compress/SevenZip/SevenZip.cs
2019-12-04 15:42:30 -08:00

1082 lines
37 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using Compress.SevenZip.Compress.BZip2;
using Compress.SevenZip.Compress.LZMA;
using Compress.SevenZip.Compress.PPmd;
using Compress.SevenZip.Filters;
using Compress.SevenZip.Structure;
using Compress.Utils;
using FileInfo = RVIO.FileInfo;
using FileStream = RVIO.FileStream;
namespace Compress.SevenZip
{
public class SevenZ : ICompress
{
private List<LocalFile> _localFiles = new List<LocalFile>();
private FileInfo _zipFileInfo;
private Stream _zipFs;
private SignatureHeader _signatureHeader;
private bool _compressed = true;
private long _baseOffset;
public string ZipFilename => _zipFileInfo != null ? _zipFileInfo.FullName : "";
public long TimeStamp => _zipFileInfo?.LastWriteTime ?? 0;
public ZipOpenType ZipOpen { get; private set; }
public ZipStatus ZipStatus { get; private set; }
public int LocalFilesCount()
{
return _localFiles.Count;
}
public string Filename(int i)
{
return _localFiles[i].FileName;
}
public ulong? LocalHeader(int i)
{
return 0;
}
public ulong UncompressedSize(int i)
{
return _localFiles[i].UncompressedSize;
}
public int StreamIndex(int i)
{
return _localFiles[i].StreamIndex;
}
public ZipReturn FileStatus(int i)
{
return _localFiles[i].FileStatus;
}
public byte[] CRC32(int i)
{
return _localFiles[i].CRC;
}
public void ZipFileCloseFailed()
{
switch (ZipOpen)
{
case ZipOpenType.Closed:
return;
case ZipOpenType.OpenRead:
ZipFileCloseReadStream();
if (_zipFs != null)
{
_zipFs.Close();
_zipFs.Dispose();
}
break;
case ZipOpenType.OpenWrite:
_zipFs.Flush();
_zipFs.Close();
_zipFs.Dispose();
if (_zipFileInfo != null)
RVIO.File.Delete(_zipFileInfo.FullName);
_zipFileInfo = null;
break;
}
ZipOpen = ZipOpenType.Closed;
}
public bool IsDirectory(int i)
{
return _localFiles[i].IsDirectory;
}
public void ZipFileAddDirectory(string filename)
{
string fName = filename;
if (fName.Substring(fName.Length - 1, 1) == @"/")
fName = fName.Substring(0, fName.Length - 1);
LocalFile lf = new LocalFile
{
FileName = fName,
UncompressedSize = 0,
IsDirectory = true,
StreamOffset = 0
};
_localFiles.Add(lf);
}
private class LocalFile
{
public string FileName;
public ulong UncompressedSize;
public bool IsDirectory;
public byte[] CRC;
public int StreamIndex;
public ulong StreamOffset;
public ZipReturn FileStatus = ZipReturn.ZipUntested;
}
#region open 7z files
public ZipReturn ZipFileOpen(string filename, long timestamp, bool readHeaders)
{
ZipFileClose();
Debug.WriteLine(filename);
#region open file stream
try
{
if (!RVIO.File.Exists(filename))
{
ZipFileClose();
return ZipReturn.ZipErrorFileNotFound;
}
_zipFileInfo = new FileInfo(filename);
if ((timestamp != -1) && (_zipFileInfo.LastWriteTime != timestamp))
{
ZipFileClose();
return ZipReturn.ZipErrorTimeStamp;
}
int errorCode = FileStream.OpenFileRead(filename, out _zipFs);
if (errorCode != 0)
{
ZipFileClose();
return ZipReturn.ZipErrorOpeningFile;
}
}
catch (PathTooLongException)
{
ZipFileClose();
return ZipReturn.ZipFileNameToLong;
}
catch (IOException)
{
ZipFileClose();
return ZipReturn.ZipErrorOpeningFile;
}
#endregion
ZipOpen = ZipOpenType.OpenRead;
ZipStatus = ZipStatus.None;
return ZipFileReadHeaders();
}
public ZipReturn ZipFileOpen(Stream inStream)
{
ZipFileClose();
_zipFileInfo = null;
_zipFs = inStream;
ZipOpen = ZipOpenType.OpenRead;
ZipStatus = ZipStatus.None;
return ZipFileReadHeaders();
}
private ZipReturn ZipFileReadHeaders()
{
try
{
SignatureHeader signatureHeader = new SignatureHeader();
if (!signatureHeader.Read(_zipFs))
{
return ZipReturn.ZipSignatureError;
}
_baseOffset = _zipFs.Position;
//_zipFs.Seek(_baseOffset + (long)signatureHeader.NextHeaderOffset, SeekOrigin.Begin);
//byte[] mainHeader = new byte[signatureHeader.NextHeaderSize];
//_zipFs.Read(mainHeader, 0, (int)signatureHeader.NextHeaderSize);
//if (!CRC.VerifyDigest(signatureHeader.NextHeaderCRC, mainHeader, 0, (uint)signatureHeader.NextHeaderSize))
// return ZipReturn.Zip64EndOfCentralDirError;
if (signatureHeader.NextHeaderSize != 0)
{
_zipFs.Seek(_baseOffset + (long)signatureHeader.NextHeaderOffset, SeekOrigin.Begin);
ZipReturn zr = Header.ReadHeaderOrPackedHeader(_zipFs, _baseOffset, out _header);
if (zr != ZipReturn.ZipGood)
{
return zr;
}
}
_zipFs.Seek(_baseOffset + (long)(signatureHeader.NextHeaderOffset + signatureHeader.NextHeaderSize), SeekOrigin.Begin);
ZipStatus = ZipStatus.None;
ZipStatus |= IsRomVault7Z() ? ZipStatus.TrrntZip : ZipStatus.None;
ZipStatus |= Istorrent7Z() ? ZipStatus.Trrnt7Zip : ZipStatus.None;
PopulateLocalFiles(out _localFiles);
return ZipReturn.ZipGood;
}
catch
{
ZipFileClose();
return ZipReturn.ZipErrorReadingFile;
}
}
private void PopulateLocalFiles(out List<LocalFile> localFiles)
{
int emptyFileIndex = 0;
int folderIndex = 0;
int unpackedStreamsIndex = 0;
ulong streamOffset = 0;
localFiles = new List<LocalFile>();
if (_header == null)
return;
for (int i = 0; i < _header.FileInfo.Names.Length; i++)
{
LocalFile lf = new LocalFile { FileName = _header.FileInfo.Names[i] };
if ((_header.FileInfo.EmptyStreamFlags == null) || !_header.FileInfo.EmptyStreamFlags[i])
{
lf.StreamIndex = folderIndex;
lf.StreamOffset = streamOffset;
lf.UncompressedSize = _header.StreamsInfo.Folders[folderIndex].UnpackedStreamInfo[unpackedStreamsIndex].UnpackedSize;
lf.CRC = Util.uinttobytes(_header.StreamsInfo.Folders[folderIndex].UnpackedStreamInfo[unpackedStreamsIndex].Crc);
streamOffset += lf.UncompressedSize;
unpackedStreamsIndex++;
if (unpackedStreamsIndex >= _header.StreamsInfo.Folders[folderIndex].UnpackedStreamInfo.Length)
{
folderIndex++;
unpackedStreamsIndex = 0;
streamOffset = 0;
}
}
else
{
lf.UncompressedSize = 0;
lf.CRC = new byte[] { 0, 0, 0, 0 };
lf.IsDirectory = (_header.FileInfo.EmptyFileFlags == null) || !_header.FileInfo.EmptyFileFlags[emptyFileIndex++];
if (lf.IsDirectory)
{
if (lf.FileName.Substring(lf.FileName.Length - 1, 1) != "/")
{
lf.FileName += "/";
}
}
}
localFiles.Add(lf);
}
}
public void ZipFileClose()
{
switch (ZipOpen)
{
case ZipOpenType.Closed:
return;
case ZipOpenType.OpenRead:
ZipFileCloseReadStream();
if (_zipFs != null)
{
_zipFs.Close();
_zipFs.Dispose();
}
ZipOpen = ZipOpenType.Closed;
return;
case ZipOpenType.OpenWrite:
CloseWriting7Zip();
if (_zipFileInfo != null)
_zipFileInfo = new FileInfo(_zipFileInfo.FullName);
break;
}
ZipOpen = ZipOpenType.Closed;
}
private Header _header;
public StringBuilder HeaderReport()
{
StringBuilder sb = new StringBuilder();
if (_header == null)
{
sb.AppendLine("Null Header");
return sb;
}
_header.Report(ref sb);
return sb;
}
// not finalized yet, so do not use
private void WriteRomVault7Zip(BinaryWriter bw, ulong headerPos, ulong headerLength, uint headerCRC)
{
const string sig = "RomVault7Z01";
byte[] RV7Zid = Util.Enc.GetBytes(sig);
// RomVault 7Zip torrent header
// 12 bytes : RomVault7Zip
// 4 bytes : HeaderCRC
// 8 bytes : HeaderPos
// 8 bytes : HeaderLength
bw.Write(RV7Zid);
bw.Write(headerCRC);
bw.Write(headerPos);
bw.Write(headerLength);
ZipStatus = ZipStatus.TrrntZip;
}
private bool IsRomVault7Z()
{
long length = _zipFs.Length;
if (length < 32)
{
return false;
}
_zipFs.Seek(length - 32, SeekOrigin.Begin);
const string sig = "RomVault7Z01";
byte[] rv7Zid = Util.Enc.GetBytes(sig);
byte[] header = new byte[12];
_zipFs.Read(header, 0, 12);
for (int i = 0; i < 12; i++)
{
if (header[i] != rv7Zid[i])
{
return false;
}
}
uint headerCRC;
ulong headerOffset;
ulong headerSize;
using (BinaryReader br = new BinaryReader(_zipFs, Encoding.UTF8, true))
{
headerCRC = br.ReadUInt32();
headerOffset = br.ReadUInt64();
headerSize = br.ReadUInt64();
}
if ((ulong)length < headerOffset)
{
return false;
}
_zipFs.Seek((long)headerOffset, SeekOrigin.Begin);
byte[] mainHeader = new byte[headerSize];
int bytesread = _zipFs.Read(mainHeader, 0, (int)headerSize);
return ((ulong)bytesread == headerSize) &&
Utils.CRC.VerifyDigest(headerCRC, mainHeader, 0, (uint)headerSize);
}
private bool Istorrent7Z()
{
const int crcsz = 128;
const int t7ZsigSize = 16 + 1 + 9 + 4 + 4;
byte[] kSignature = { (byte)'7', (byte)'z', 0xBC, 0xAF, 0x27, 0x1C };
int kSignatureSize = kSignature.Length;
const string sig = "\xa9\x9f\xd1\x57\x08\xa9\xd7\xea\x29\x64\xb2\x36\x1b\x83\x52\x33\x01torrent7z_0.9beta";
byte[] t7Zid = Util.Enc.GetBytes(sig);
int t7ZidSize = t7Zid.Length;
const int tmpbufsize = 256 + t7ZsigSize + 8 + 4;
byte[] buffer = new byte[tmpbufsize];
// read fist 128 bytes, pad with zeros if less bytes
int bufferPos = 0;
_zipFs.Seek(0, SeekOrigin.Begin);
int ar = _zipFs.Read(buffer, bufferPos, crcsz);
if (ar < crcsz)
{
Util.memset(buffer, bufferPos + ar, 0, crcsz - ar);
}
bufferPos = crcsz;
long foffs = _zipFs.Length;
int endReadLength = crcsz + t7ZsigSize + 4;
foffs = foffs < endReadLength ? 0 : foffs - endReadLength;
_zipFs.Seek(foffs, SeekOrigin.Begin);
ar = _zipFs.Read(buffer, bufferPos, endReadLength);
if (ar < endReadLength)
{
if (ar >= t7ZsigSize + 4)
{
ar -= t7ZsigSize + 4;
}
if (ar < kSignatureSize)
{
ar = kSignatureSize;
}
Util.memset(buffer, bufferPos + ar, 0, crcsz - ar);
Util.memcpyr(buffer, crcsz * 2 + 8, buffer, bufferPos + ar, t7ZsigSize + 4);
}
else
{
Util.memcpyr(buffer, crcsz * 2 + 8, buffer, crcsz * 2, t7ZsigSize + 4);
}
foffs = _zipFs.Length;
foffs -= t7ZsigSize + 4;
//memcpy(buffer, crcsz * 2, &foffs, 8);
buffer[crcsz * 2 + 0] = (byte)((foffs >> 0) & 0xff);
buffer[crcsz * 2 + 1] = (byte)((foffs >> 8) & 0xff);
buffer[crcsz * 2 + 2] = (byte)((foffs >> 16) & 0xff);
buffer[crcsz * 2 + 3] = (byte)((foffs >> 24) & 0xff);
buffer[crcsz * 2 + 4] = 0;
buffer[crcsz * 2 + 5] = 0;
buffer[crcsz * 2 + 6] = 0;
buffer[crcsz * 2 + 7] = 0;
if (Util.memcmp(buffer, 0, kSignature, kSignatureSize))
{
t7Zid[16] = buffer[crcsz * 2 + 4 + 8 + 16];
if (Util.memcmp(buffer, crcsz * 2 + 4 + 8, t7Zid, t7ZidSize))
{
uint inCrc32 = (uint)(buffer[crcsz * 2 + 8 + 0] +
(buffer[crcsz * 2 + 8 + 1] << 8) +
(buffer[crcsz * 2 + 8 + 2] << 16) +
(buffer[crcsz * 2 + 8 + 3] << 24));
buffer[crcsz * 2 + 8 + 0] = 0xff;
buffer[crcsz * 2 + 8 + 1] = 0xff;
buffer[crcsz * 2 + 8 + 2] = 0xff;
buffer[crcsz * 2 + 8 + 3] = 0xff;
uint calcCrc32 = Utils.CRC.CalculateDigest(buffer, 0, crcsz * 2 + 8 + t7ZsigSize + 4);
if (inCrc32 == calcCrc32)
{
return true;
}
}
}
return false;
}
#endregion
#region read 7z file
private int _streamIndex = -1;
private Stream _stream;
public ZipReturn ZipFileOpenReadStream(int index, out Stream stream, out ulong unCompressedSize)
{
Debug.WriteLine("Opening File " + _localFiles[index].FileName);
stream = null;
unCompressedSize = 0;
try
{
if (ZipOpen != ZipOpenType.OpenRead)
{
return ZipReturn.ZipErrorGettingDataStream;
}
if (IsDirectory(index))
{
return ZipReturn.ZipTryingToAccessADirectory;
}
unCompressedSize = _localFiles[index].UncompressedSize;
int thisStreamIndex = _localFiles[index].StreamIndex;
ulong streamOffset = _localFiles[index].StreamOffset;
if ((thisStreamIndex == _streamIndex) && (streamOffset >= (ulong)_stream.Position))
{
stream = _stream;
stream.Seek((long)_localFiles[index].StreamOffset - _stream.Position, SeekOrigin.Current);
return ZipReturn.ZipGood;
}
ZipFileCloseReadStream();
_streamIndex = thisStreamIndex;
Folder folder = _header.StreamsInfo.Folders[_streamIndex];
// first make the List of Decompressors streams
int codersNeeded = folder.Coders.Length;
List<InStreamSourceInfo> allInputStreams = new List<InStreamSourceInfo>();
for (int i = 0; i < codersNeeded; i++)
{
folder.Coders[i].DecoderStream = null;
allInputStreams.AddRange(folder.Coders[i].InputStreamsSourceInfo);
}
// now use the binding pairs to links the outputs to the inputs
int bindPairsCount = folder.BindPairs.Length;
for (int i = 0; i < bindPairsCount; i++)
{
allInputStreams[(int)folder.BindPairs[i].InIndex].InStreamSource = InStreamSource.CompStreamOutput;
allInputStreams[(int)folder.BindPairs[i].InIndex].InStreamIndex = folder.BindPairs[i].OutIndex;
folder.Coders[(int)folder.BindPairs[i].OutIndex].OutputUsedInternally = true;
}
// next use the stream indises to connect the remaining input streams from the sourcefile
int packedStreamsCount = folder.PackedStreamIndices.Length;
for (int i = 0; i < packedStreamsCount; i++)
{
ulong packedStreamIndex = (ulong)i + folder.PackedStreamIndexBase;
// create and open the source file stream if needed
if (_header.StreamsInfo.PackedStreams[packedStreamIndex].PackedStream == null)
{
_header.StreamsInfo.PackedStreams[packedStreamIndex].PackedStream = CloneStream(_zipFs);
}
_header.StreamsInfo.PackedStreams[packedStreamIndex].PackedStream.Seek(
_baseOffset + (long)_header.StreamsInfo.PackedStreams[packedStreamIndex].StreamPosition, SeekOrigin.Begin);
allInputStreams[(int)folder.PackedStreamIndices[i]].InStreamSource = InStreamSource.FileStream;
allInputStreams[(int)folder.PackedStreamIndices[i]].InStreamIndex = packedStreamIndex;
}
List<Stream> inputCoders = new List<Stream>();
bool allCodersComplete = false;
while (!allCodersComplete)
{
allCodersComplete = true;
for (int i = 0; i < codersNeeded; i++)
{
Coder coder = folder.Coders[i];
// check is decoder already processed
if (coder.DecoderStream != null)
{
continue;
}
inputCoders.Clear();
for (int j = 0; j < (int)coder.NumInStreams; j++)
{
if (coder.InputStreamsSourceInfo[j].InStreamSource == InStreamSource.FileStream)
{
inputCoders.Add(_header.StreamsInfo.PackedStreams[coder.InputStreamsSourceInfo[j].InStreamIndex].PackedStream);
}
else if (coder.InputStreamsSourceInfo[j].InStreamSource == InStreamSource.CompStreamOutput)
{
if (folder.Coders[coder.InputStreamsSourceInfo[j].InStreamIndex].DecoderStream == null)
{
break;
}
inputCoders.Add(folder.Coders[coder.InputStreamsSourceInfo[j].InStreamIndex].DecoderStream);
}
else
{
// unknown input type so error
return ZipReturn.ZipDecodeError;
}
}
if (inputCoders.Count == (int)coder.NumInStreams)
{
// all inputs streams are available to make the decoder stream
switch (coder.DecoderType)
{
case DecompressType.Stored:
coder.DecoderStream = inputCoders[0];
break;
case DecompressType.Delta:
coder.DecoderStream = new Delta(folder.Coders[i].Properties, inputCoders[0]);
break;
case DecompressType.LZMA:
coder.DecoderStream = new LzmaStream(folder.Coders[i].Properties, inputCoders[0]);
break;
case DecompressType.LZMA2:
coder.DecoderStream = new LzmaStream(folder.Coders[i].Properties, inputCoders[0]);
break;
case DecompressType.PPMd:
coder.DecoderStream = new PpmdStream(new PpmdProperties(folder.Coders[i].Properties), inputCoders[0], false);
break;
case DecompressType.BZip2:
coder.DecoderStream = new CBZip2InputStream(inputCoders[0], false);
break;
case DecompressType.BCJ:
coder.DecoderStream = new BCJFilter(false, inputCoders[0]);
break;
case DecompressType.BCJ2:
coder.DecoderStream = new BCJ2Filter(inputCoders[0], inputCoders[1], inputCoders[2], inputCoders[3]);
break;
default:
return ZipReturn.ZipDecodeError;
}
}
// if skipped a coder need to loop round again
if (coder.DecoderStream == null)
{
allCodersComplete = false;
}
}
}
// find the final output stream and return it.
int outputStream = -1;
for (int i = 0; i < codersNeeded; i++)
{
Coder coder = folder.Coders[i];
if (!coder.OutputUsedInternally)
{
outputStream = i;
}
}
stream = folder.Coders[outputStream].DecoderStream;
stream.Seek((long)_localFiles[index].StreamOffset, SeekOrigin.Current);
_stream = stream;
return ZipReturn.ZipGood;
}
catch (Exception e)
{
return ZipReturn.ZipErrorGettingDataStream;
}
}
private Stream CloneStream(Stream s)
{
switch (s)
{
case System.IO.FileStream _:
int errorCode = FileStream.OpenFileRead(ZipFilename, out Stream streamOut);
return errorCode != 0 ? null : streamOut;
case MemoryStream memStream:
long pos = memStream.Position;
memStream.Position = 0;
byte[] newStream = new byte[memStream.Length];
memStream.Read(newStream, 0, (int)memStream.Length);
MemoryStream ret = new MemoryStream(newStream, false);
memStream.Position = pos;
return ret;
}
return null;
}
public ZipReturn ZipFileCloseReadStream()
{
if (_streamIndex != -1)
{
Folder folder = _header.StreamsInfo.Folders[_streamIndex];
foreach (Coder c in folder.Coders)
{
Stream ds = c?.DecoderStream;
if (ds == null)
{
continue;
}
ds.Close();
ds.Dispose();
c.DecoderStream = null;
}
}
_streamIndex = -1;
if (_header?.StreamsInfo != null)
{
foreach (PackedStreamInfo psi in _header.StreamsInfo.PackedStreams)
{
if (psi?.PackedStream == null)
{
continue;
}
psi.PackedStream.Close();
psi.PackedStream.Dispose();
psi.PackedStream = null;
}
}
return ZipReturn.ZipGood;
}
#endregion
#region write 7z File
private LzmaStream _lzmaStream;
private ulong _packStreamStart;
private ulong _packStreamSize;
private ulong _unpackedStreamSize;
private byte[] _codeMSbytes;
public void ZipFileAddDirectory()
{
// do nothing here for 7zip
}
public ZipReturn ZipFileCreate(string newFilename)
{
return ZipFileCreate(newFilename, true);
}
public ZipReturn ZipFileCreateFromUncompressedSize(string newFilename, ulong unCompressedSize)
{
return ZipFileCreate(newFilename,true, GetDictionarySizeFromUncompressedSize(unCompressedSize));
}
public ZipReturn ZipFileCreate(string newFilename, bool compressOutput, int dictionarySize = 1 << 24, int numFastBytes = 64)
{
if (ZipOpen != ZipOpenType.Closed)
{
return ZipReturn.ZipFileAlreadyOpen;
}
DirUtil.CreateDirForFile(newFilename);
_zipFileInfo = new FileInfo(newFilename);
int errorCode = FileStream.OpenFileWrite(newFilename, out _zipFs);
if (errorCode != 0)
{
ZipFileClose();
return ZipReturn.ZipErrorOpeningFile;
}
ZipOpen = ZipOpenType.OpenWrite;
_signatureHeader = new SignatureHeader();
_header = new Header();
using (BinaryWriter bw = new BinaryWriter(_zipFs, Encoding.UTF8, true))
{
_signatureHeader.Write(bw);
}
_compressed = compressOutput;
_unpackedStreamSize = 0;
if (_compressed)
{
LzmaEncoderProperties ep = new LzmaEncoderProperties(true, dictionarySize, numFastBytes);
_lzmaStream = new LzmaStream(ep, false, _zipFs);
_codeMSbytes = _lzmaStream.Properties;
_packStreamStart = (ulong)_zipFs.Position;
}
return ZipReturn.ZipGood;
}
public ZipReturn ZipFileOpenWriteStream(bool raw, bool trrntzip, string filename, ulong uncompressedSize, ushort compressionMethod, out Stream stream)
{
return ZipFileOpenWriteStream(filename, uncompressedSize, out stream);
}
private ZipReturn ZipFileOpenWriteStream(string filename, ulong uncompressedSize, out Stream stream)
{
LocalFile lf = new LocalFile
{
FileName = filename,
UncompressedSize = uncompressedSize,
StreamOffset = (ulong)(_zipFs.Position - _signatureHeader.BaseOffset)
};
_unpackedStreamSize += uncompressedSize;
_localFiles.Add(lf);
stream = _compressed ? _lzmaStream : _zipFs;
return ZipReturn.ZipGood;
}
public ZipReturn ZipFileCloseWriteStream(byte[] crc32)
{
_localFiles[_localFiles.Count - 1].CRC = new[] { crc32[3], crc32[2], crc32[1], crc32[0] };
return ZipReturn.ZipGood;
}
private void CloseWriting7Zip()
{
if (_compressed)
{
_lzmaStream.Close();
}
_packStreamSize = (ulong)_zipFs.Position - _packStreamStart;
Create7ZStructure();
byte[] newHeaderByte;
using (Stream headerMem = new MemoryStream())
{
using (BinaryWriter headerBw = new BinaryWriter(headerMem, Encoding.UTF8, true))
{
_header.WriteHeader(headerBw);
newHeaderByte = new byte[headerMem.Length];
headerMem.Position = 0;
headerMem.Read(newHeaderByte, 0, newHeaderByte.Length);
}
}
uint mainHeaderCRC = Utils.CRC.CalculateDigest(newHeaderByte, 0, (uint)newHeaderByte.Length);
ulong headerpos = (ulong)_zipFs.Position;
_zipFs.Write(newHeaderByte,0,newHeaderByte.Length);
using (BinaryWriter bw = new BinaryWriter(_zipFs, Encoding.UTF8, true))
{
_signatureHeader.WriteFinal(bw, headerpos, (ulong)newHeaderByte.Length, mainHeaderCRC);
WriteRomVault7Zip(bw, headerpos, (ulong)newHeaderByte.Length, mainHeaderCRC);
}
_zipFs.Flush();
_zipFs.Close();
_zipFs.Dispose();
}
private void Create7ZStructure()
{
int fileCount = _localFiles.Count;
//FileInfo
_header.FileInfo = new Structure.FileInfo
{
Names = new string[fileCount]
};
ulong emptyStreamCount = 0;
ulong emptyFileCount = 0;
for (int i = 0; i < fileCount; i++)
{
_header.FileInfo.Names[i] = _localFiles[i].FileName;
if (_localFiles[i].UncompressedSize != 0)
{
continue;
}
if (!_localFiles[i].IsDirectory)
{
emptyFileCount += 1;
}
emptyStreamCount += 1;
}
ulong outFileCount = (ulong)_localFiles.Count - emptyStreamCount;
_header.FileInfo.EmptyStreamFlags = null;
_header.FileInfo.EmptyFileFlags = null;
_header.FileInfo.Attributes = null;
if (emptyStreamCount > 0)
{
if (emptyStreamCount != emptyFileCount) //then we found directories and need to set the attributes
{
_header.FileInfo.Attributes = new uint[fileCount];
}
if (emptyFileCount > 0)
{
_header.FileInfo.EmptyFileFlags = new bool[emptyStreamCount];
}
emptyStreamCount = 0;
_header.FileInfo.EmptyStreamFlags = new bool[fileCount];
for (int i = 0; i < fileCount; i++)
{
if (_localFiles[i].UncompressedSize != 0)
{
continue;
}
if (_localFiles[i].IsDirectory)
{
if (_header.FileInfo.Attributes != null)
_header.FileInfo.Attributes[i] = 0x10; // set attributes to directory
}
else
{
if (_header.FileInfo.EmptyFileFlags != null)
_header.FileInfo.EmptyFileFlags[emptyStreamCount] = true; // set empty file flag
}
_header.FileInfo.EmptyStreamFlags[i] = true;
emptyStreamCount += 1;
}
}
//StreamsInfo
_header.StreamsInfo = new StreamsInfo { PackPosition = 0 };
//StreamsInfo.PackedStreamsInfo
if (_compressed)
{
_header.StreamsInfo.PackedStreams = new PackedStreamInfo[1];
_header.StreamsInfo.PackedStreams[0] = new PackedStreamInfo { PackedSize = _packStreamSize };
}
else
{
_header.StreamsInfo.PackedStreams = new PackedStreamInfo[outFileCount];
int fileIndex = 0;
for (int i = 0; i < fileCount; i++)
{
if (_localFiles[i].UncompressedSize == 0)
{
continue;
}
_header.StreamsInfo.PackedStreams[fileIndex++] = new PackedStreamInfo { PackedSize = _localFiles[i].UncompressedSize };
}
}
//StreamsInfo.PackedStreamsInfo, no CRC or StreamPosition required
if (_compressed)
{
//StreamsInfo.Folders
_header.StreamsInfo.Folders = new Folder[1];
Folder folder = new Folder { Coders = new Coder[1] };
//StreamsInfo.Folders.Coder
// flags 0x23
folder.Coders[0] = new Coder
{
Method = new byte[] { 3, 1, 1 },
NumInStreams = 1,
NumOutStreams = 1,
Properties = _codeMSbytes
};
folder.BindPairs = null;
folder.PackedStreamIndices = new[] { (ulong)0 };
folder.UnpackedStreamSizes = new[] { _unpackedStreamSize };
folder.UnpackCRC = null;
folder.UnpackedStreamInfo = new UnpackedStreamInfo[outFileCount];
int fileIndex = 0;
for (int i = 0; i < fileCount; i++)
{
if (_localFiles[i].UncompressedSize == 0)
{
continue;
}
UnpackedStreamInfo unpackedStreamInfo = new UnpackedStreamInfo
{
UnpackedSize = _localFiles[i].UncompressedSize,
Crc = Util.bytestouint(_localFiles[i].CRC)
};
folder.UnpackedStreamInfo[fileIndex++] = unpackedStreamInfo;
}
_header.StreamsInfo.Folders[0] = folder;
}
else
{
_header.StreamsInfo.Folders = new Folder[outFileCount];
int fileIndex = 0;
for (int i = 0; i < fileCount; i++)
{
if (_localFiles[i].UncompressedSize == 0)
{
continue;
}
Folder folder = new Folder { Coders = new Coder[1] };
//StreamsInfo.Folders.Coder
// flags 0x01
folder.Coders[0] = new Coder
{
Method = new byte[] { 0 },
NumInStreams = 1,
NumOutStreams = 1,
Properties = null
};
folder.BindPairs = null;
folder.PackedStreamIndices = new[] { (ulong)i };
folder.UnpackedStreamSizes = new[] { _localFiles[i].UncompressedSize };
folder.UnpackCRC = null;
folder.UnpackedStreamInfo = new UnpackedStreamInfo[1];
UnpackedStreamInfo unpackedStreamInfo = new UnpackedStreamInfo
{
UnpackedSize = _localFiles[i].UncompressedSize,
Crc = Util.bytestouint(_localFiles[i].CRC)
};
folder.UnpackedStreamInfo[0] = unpackedStreamInfo;
_header.StreamsInfo.Folders[fileIndex++] = folder;
}
}
}
#endregion
private static readonly int[] DictionarySizes =
{
0x10000,
0x18000,
0x20000,
0x30000,
0x40000,
0x60000,
0x80000,
0xc0000,
0x100000,
0x180000,
0x200000,
0x300000,
0x400000,
0x600000,
0x800000,
0xc00000,
0x1000000,
0x1800000,
0x2000000,
0x3000000,
0x4000000,
0x6000000
};
private static int GetDictionarySizeFromUncompressedSize(ulong unCompressedSize)
{
foreach (int v in DictionarySizes)
{
if ((ulong)v >= unCompressedSize)
return v;
}
return DictionarySizes[DictionarySizes.Length - 1];
}
}
}