Files
SabreTools/SabreTools.Library/External/Compress/SevenZip/SevenZipWriteClose.cs

293 lines
10 KiB
C#
Raw Normal View History

using System.IO;
using System.Text;
using Compress.SevenZip.Compress.LZMA;
using Compress.SevenZip.Structure;
using Compress.Utils;
using Zstandard.Net;
namespace Compress.SevenZip
{
public partial class SevenZ
{
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];
//StreamsInfo.Folders.Coder
// flags 0x23
Folder folder = new Folder
{
BindPairs = null,
Coders = new[] {
new Coder {
Method = new byte[] { 3, 1, 1 },
NumInStreams = 1,
NumOutStreams = 1,
Properties = _codeMSbytes
}
},
PackedStreamIndices = new[] { (ulong)0 },
UnpackedStreamSizes = new[] { _unpackedStreamSize },
UnpackedStreamInfo = new UnpackedStreamInfo[outFileCount],
UnpackCRC = null
};
switch (_lzmaStream)
{
case LzmaStream _:
folder.Coders[0].Method = new byte[] { 3, 1, 1 };
break;
case ZstandardStream _:
folder.Coders[0].Method = new byte[] { 4, 247, 17, 1 };
break;
}
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
{
BindPairs = null,
Coders = new[] {
new Coder {
Method = new byte[] {0},
NumInStreams = 1,
NumOutStreams = 1,
Properties = null
}
},
PackedStreamIndices = new[] { (ulong)i },
UnpackedStreamSizes = new[] { _localFiles[i].UncompressedSize },
UnpackCRC = null,
UnpackedStreamInfo = new[] {
new UnpackedStreamInfo {
UnpackedSize = _localFiles[i].UncompressedSize,
Crc = Util.bytestouint(_localFiles[i].CRC)
}
}
};
_header.StreamsInfo.Folders[fileIndex++] = folder;
}
}
}
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 = CRC.CalculateDigest(newHeaderByte, 0, (uint)newHeaderByte.Length);
#region Header Compression
long packedHeaderPos = _zipFs.Position;
LzmaEncoderProperties ep = new LzmaEncoderProperties(true, GetDictionarySizeFromUncompressedSize((ulong)newHeaderByte.Length), 64);
LzmaStream lzs = new LzmaStream(ep, false, _zipFs);
byte[] lzmaStreamProperties = lzs.Properties;
lzs.Write(newHeaderByte, 0, newHeaderByte.Length);
lzs.Close();
StreamsInfo streamsInfo = new StreamsInfo
{
PackPosition = (ulong)(packedHeaderPos - _baseOffset),
Folders = new[] {
new Folder {
BindPairs = new BindPair[0],
Coders = new [] {
new Coder {
Method = new byte[] { 3, 1, 1 },
NumInStreams = 1,
NumOutStreams = 1,
Properties = lzmaStreamProperties
}
},
UnpackedStreamSizes = new[] {(ulong) newHeaderByte.Length},
UnpackCRC = mainHeaderCRC
}
},
PackedStreams = new[] {
new PackedStreamInfo
{
PackedSize = (ulong)(_zipFs.Position - packedHeaderPos),
StreamPosition = 0
}
}
};
using (Stream headerMem = new MemoryStream())
{
using (BinaryWriter bw = new BinaryWriter(headerMem, Encoding.UTF8, true))
{
bw.Write((byte)HeaderProperty.kEncodedHeader);
streamsInfo.WriteHeader(bw);
newHeaderByte = new byte[headerMem.Length];
headerMem.Position = 0;
headerMem.Read(newHeaderByte, 0, newHeaderByte.Length);
}
}
mainHeaderCRC = CRC.CalculateDigest(newHeaderByte, 0, (uint)newHeaderByte.Length);
#endregion
using (BinaryWriter bw = new BinaryWriter(_zipFs, Encoding.UTF8, true))
{
ulong headerPosition = (ulong)_zipFs.Position + 32; //tzip header is 32 bytes
WriteRomVault7Zip(bw, headerPosition, (ulong)newHeaderByte.Length, mainHeaderCRC);
_zipFs.Write(newHeaderByte, 0, newHeaderByte.Length);
_signatureHeader.WriteFinal(bw, headerPosition, (ulong)newHeaderByte.Length, mainHeaderCRC);
}
_zipFs.Flush();
_zipFs.Close();
_zipFs.Dispose();
}
}
}