From 7e7429e3d246d369d7c12a784c73a124bce60d03 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Wed, 15 Apr 2009 03:58:10 +0000 Subject: [PATCH] - Added support for 7z archives. SVN r1545 (trunk) --- docs/rh-log.txt | 5 +- lzma/C/7zBuf.c | 36 + lzma/C/7zBuf.h | 31 + lzma/C/7zCrc.c | 35 + lzma/C/7zCrc.h | 24 + lzma/C/7zStream.c | 169 +++++ lzma/C/Archive/7z/7zDecode.c | 254 +++++++ lzma/C/Archive/7z/7zDecode.h | 13 + lzma/C/Archive/7z/7zExtract.c | 93 +++ lzma/C/Archive/7z/7zExtract.h | 41 ++ lzma/C/Archive/7z/7zHeader.c | 6 + lzma/C/Archive/7z/7zHeader.h | 57 ++ lzma/C/Archive/7z/7zIn.c | 1204 +++++++++++++++++++++++++++++++++ lzma/C/Archive/7z/7zIn.h | 41 ++ lzma/C/Archive/7z/7zItem.c | 127 ++++ lzma/C/Archive/7z/7zItem.h | 84 +++ lzma/C/Bcj2.c | 132 ++++ lzma/C/Bcj2.h | 30 + lzma/C/Bra.h | 60 ++ lzma/C/Bra86.c | 85 +++ lzma/C/CpuArch.h | 69 ++ lzma/C/Types.h | 8 + lzma/CMakeLists.txt | 10 + lzma/lzmalib.vcproj | 84 +++ src/files.cpp | 2 +- src/w_wad.cpp | 625 ++++++++++++----- src/w_wad.h | 7 +- 27 files changed, 3155 insertions(+), 177 deletions(-) create mode 100644 lzma/C/7zBuf.c create mode 100644 lzma/C/7zBuf.h create mode 100644 lzma/C/7zCrc.c create mode 100644 lzma/C/7zCrc.h create mode 100644 lzma/C/7zStream.c create mode 100644 lzma/C/Archive/7z/7zDecode.c create mode 100644 lzma/C/Archive/7z/7zDecode.h create mode 100644 lzma/C/Archive/7z/7zExtract.c create mode 100644 lzma/C/Archive/7z/7zExtract.h create mode 100644 lzma/C/Archive/7z/7zHeader.c create mode 100644 lzma/C/Archive/7z/7zHeader.h create mode 100644 lzma/C/Archive/7z/7zIn.c create mode 100644 lzma/C/Archive/7z/7zIn.h create mode 100644 lzma/C/Archive/7z/7zItem.c create mode 100644 lzma/C/Archive/7z/7zItem.h create mode 100644 lzma/C/Bcj2.c create mode 100644 lzma/C/Bcj2.h create mode 100644 lzma/C/Bra.h create mode 100644 lzma/C/Bra86.c create mode 100644 lzma/C/CpuArch.h diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 1bb777d96..390e2104d 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,4 +1,7 @@ -April 13, 2009 (Changes by Graf Zahl) +April 14, 2009 +- Added support for 7z archives. + +April 13, 2009 (Changes by Graf Zahl) - Added -noautoload option. - Added default Raven automap colors set. Needs to be tested because I can't compare against the DOS version myself. diff --git a/lzma/C/7zBuf.c b/lzma/C/7zBuf.c new file mode 100644 index 000000000..a35fa2f98 --- /dev/null +++ b/lzma/C/7zBuf.c @@ -0,0 +1,36 @@ +/* 7zBuf.c -- Byte Buffer +2008-03-28 +Igor Pavlov +Public domain */ + +#include "7zBuf.h" + +void Buf_Init(CBuf *p) +{ + p->data = 0; + p->size = 0; +} + +int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc) +{ + p->size = 0; + if (size == 0) + { + p->data = 0; + return 1; + } + p->data = (Byte *)alloc->Alloc(alloc, size); + if (p->data != 0) + { + p->size = size; + return 1; + } + return 0; +} + +void Buf_Free(CBuf *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->data); + p->data = 0; + p->size = 0; +} diff --git a/lzma/C/7zBuf.h b/lzma/C/7zBuf.h new file mode 100644 index 000000000..49b2354bd --- /dev/null +++ b/lzma/C/7zBuf.h @@ -0,0 +1,31 @@ +/* 7zBuf.h -- Byte Buffer +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __7Z_BUF_H +#define __7Z_BUF_H + +#include "Types.h" + +typedef struct +{ + Byte *data; + size_t size; +} CBuf; + +void Buf_Init(CBuf *p); +int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc); +void Buf_Free(CBuf *p, ISzAlloc *alloc); + +typedef struct +{ + Byte *data; + size_t size; + size_t pos; +} CDynBuf; + +void DynBuf_Construct(CDynBuf *p); +void DynBuf_SeekToBeg(CDynBuf *p); +int DynBuf_Write(CDynBuf *p, const Byte *buf, size_t size, ISzAlloc *alloc); +void DynBuf_Free(CDynBuf *p, ISzAlloc *alloc); + +#endif diff --git a/lzma/C/7zCrc.c b/lzma/C/7zCrc.c new file mode 100644 index 000000000..92c47de0d --- /dev/null +++ b/lzma/C/7zCrc.c @@ -0,0 +1,35 @@ +/* 7zCrc.c -- CRC32 calculation +2008-08-05 +Igor Pavlov +Public domain */ + +#include "7zCrc.h" + +#define kCrcPoly 0xEDB88320 +UInt32 g_CrcTable[256]; + +void MY_FAST_CALL CrcGenerateTable(void) +{ + UInt32 i; + for (i = 0; i < 256; i++) + { + UInt32 r = i; + int j; + for (j = 0; j < 8; j++) + r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); + g_CrcTable[i] = r; + } +} + +UInt32 MY_FAST_CALL CrcUpdate(UInt32 v, const void *data, size_t size) +{ + const Byte *p = (const Byte *)data; + for (; size > 0 ; size--, p++) + v = CRC_UPDATE_BYTE(v, *p); + return v; +} + +UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size) +{ + return CrcUpdate(CRC_INIT_VAL, data, size) ^ 0xFFFFFFFF; +} diff --git a/lzma/C/7zCrc.h b/lzma/C/7zCrc.h new file mode 100644 index 000000000..ae230991e --- /dev/null +++ b/lzma/C/7zCrc.h @@ -0,0 +1,24 @@ +/* 7zCrc.h -- CRC32 calculation +2008-03-13 +Igor Pavlov +Public domain */ + +#ifndef __7Z_CRC_H +#define __7Z_CRC_H + +#include + +#include "Types.h" + +extern UInt32 g_CrcTable[]; + +void MY_FAST_CALL CrcGenerateTable(void); + +#define CRC_INIT_VAL 0xFFFFFFFF +#define CRC_GET_DIGEST(crc) ((crc) ^ 0xFFFFFFFF) +#define CRC_UPDATE_BYTE(crc, b) (g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) + +UInt32 MY_FAST_CALL CrcUpdate(UInt32 crc, const void *data, size_t size); +UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size); + +#endif diff --git a/lzma/C/7zStream.c b/lzma/C/7zStream.c new file mode 100644 index 000000000..a0a2405d0 --- /dev/null +++ b/lzma/C/7zStream.c @@ -0,0 +1,169 @@ +/* 7zStream.c -- 7z Stream functions +2008-11-23 : Igor Pavlov : Public domain */ + +#include + +#include "Types.h" + +SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType) +{ + while (size != 0) + { + size_t processed = size; + RINOK(stream->Read(stream, buf, &processed)); + if (processed == 0) + return errorType; + buf = (void *)((Byte *)buf + processed); + size -= processed; + } + return SZ_OK; +} + +SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size) +{ + return SeqInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF); +} + +SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf) +{ + size_t processed = 1; + RINOK(stream->Read(stream, buf, &processed)); + return (processed == 1) ? SZ_OK : SZ_ERROR_INPUT_EOF; +} + +SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset) +{ + Int64 t = offset; + return stream->Seek(stream, &t, SZ_SEEK_SET); +} + +SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size) +{ + void *lookBuf; + if (*size == 0) + return SZ_OK; + RINOK(stream->Look(stream, &lookBuf, size)); + memcpy(buf, lookBuf, *size); + return stream->Skip(stream, *size); +} + +SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType) +{ + while (size != 0) + { + size_t processed = size; + RINOK(stream->Read(stream, buf, &processed)); + if (processed == 0) + return errorType; + buf = (void *)((Byte *)buf + processed); + size -= processed; + } + return SZ_OK; +} + +SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size) +{ + return LookInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF); +} + +static SRes LookToRead_Look_Lookahead(void *pp, void **buf, size_t *size) +{ + SRes res = SZ_OK; + CLookToRead *p = (CLookToRead *)pp; + size_t size2 = p->size - p->pos; + if (size2 == 0 && *size > 0) + { + p->pos = 0; + size2 = LookToRead_BUF_SIZE; + res = p->realStream->Read(p->realStream, p->buf, &size2); + p->size = size2; + } + if (size2 < *size) + *size = size2; + *buf = p->buf + p->pos; + return res; +} + +static SRes LookToRead_Look_Exact(void *pp, void **buf, size_t *size) +{ + SRes res = SZ_OK; + CLookToRead *p = (CLookToRead *)pp; + size_t size2 = p->size - p->pos; + if (size2 == 0 && *size > 0) + { + p->pos = 0; + if (*size > LookToRead_BUF_SIZE) + *size = LookToRead_BUF_SIZE; + res = p->realStream->Read(p->realStream, p->buf, size); + size2 = p->size = *size; + } + if (size2 < *size) + *size = size2; + *buf = p->buf + p->pos; + return res; +} + +static SRes LookToRead_Skip(void *pp, size_t offset) +{ + CLookToRead *p = (CLookToRead *)pp; + p->pos += offset; + return SZ_OK; +} + +static SRes LookToRead_Read(void *pp, void *buf, size_t *size) +{ + CLookToRead *p = (CLookToRead *)pp; + size_t rem = p->size - p->pos; + if (rem == 0) + return p->realStream->Read(p->realStream, buf, size); + if (rem > *size) + rem = *size; + memcpy(buf, p->buf + p->pos, rem); + p->pos += rem; + *size = rem; + return SZ_OK; +} + +static SRes LookToRead_Seek(void *pp, Int64 *pos, ESzSeek origin) +{ + CLookToRead *p = (CLookToRead *)pp; + p->pos = p->size = 0; + return p->realStream->Seek(p->realStream, pos, origin); +} + +void LookToRead_CreateVTable(CLookToRead *p, int lookahead) +{ + p->s.Look = lookahead ? + LookToRead_Look_Lookahead : + LookToRead_Look_Exact; + p->s.Skip = LookToRead_Skip; + p->s.Read = LookToRead_Read; + p->s.Seek = LookToRead_Seek; +} + +void LookToRead_Init(CLookToRead *p) +{ + p->pos = p->size = 0; +} + +static SRes SecToLook_Read(void *pp, void *buf, size_t *size) +{ + CSecToLook *p = (CSecToLook *)pp; + return LookInStream_LookRead(p->realStream, buf, size); +} + +void SecToLook_CreateVTable(CSecToLook *p) +{ + p->s.Read = SecToLook_Read; +} + +static SRes SecToRead_Read(void *pp, void *buf, size_t *size) +{ + CSecToRead *p = (CSecToRead *)pp; + return p->realStream->Read(p->realStream, buf, size); +} + +void SecToRead_CreateVTable(CSecToRead *p) +{ + p->s.Read = SecToRead_Read; +} diff --git a/lzma/C/Archive/7z/7zDecode.c b/lzma/C/Archive/7z/7zDecode.c new file mode 100644 index 000000000..097d2ea3c --- /dev/null +++ b/lzma/C/Archive/7z/7zDecode.c @@ -0,0 +1,254 @@ +/* 7zDecode.c -- Decoding from 7z folder +2008-11-23 : Igor Pavlov : Public domain */ + +#include + +#include "../../Bcj2.h" +#include "../../Bra.h" +#include "../../LzmaDec.h" +#include "7zDecode.h" + +#define k_Copy 0 +#define k_LZMA 0x30101 +#define k_BCJ 0x03030103 +#define k_BCJ2 0x0303011B + +static SRes SzDecodeLzma(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream, + Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) +{ + CLzmaDec state; + SRes res = SZ_OK; + + LzmaDec_Construct(&state); + RINOK(LzmaDec_AllocateProbs(&state, coder->Props.data, (unsigned)coder->Props.size, allocMain)); + state.dic = outBuffer; + state.dicBufSize = outSize; + LzmaDec_Init(&state); + + for (;;) + { + Byte *inBuf = NULL; + size_t lookahead = (1 << 18); + if (lookahead > inSize) + lookahead = (size_t)inSize; + res = inStream->Look((void *)inStream, (void **)&inBuf, &lookahead); + if (res != SZ_OK) + break; + + { + SizeT inProcessed = (SizeT)lookahead, dicPos = state.dicPos; + ELzmaStatus status; + res = LzmaDec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status); + lookahead -= inProcessed; + inSize -= inProcessed; + if (res != SZ_OK) + break; + if (state.dicPos == state.dicBufSize || (inProcessed == 0 && dicPos == state.dicPos)) + { + if (state.dicBufSize != outSize || lookahead != 0 || + (status != LZMA_STATUS_FINISHED_WITH_MARK && + status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)) + res = SZ_ERROR_DATA; + break; + } + res = inStream->Skip((void *)inStream, inProcessed); + if (res != SZ_OK) + break; + } + } + + LzmaDec_FreeProbs(&state, allocMain); + return res; +} + +static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer) +{ + while (inSize > 0) + { + void *inBuf; + size_t curSize = (1 << 18); + if (curSize > inSize) + curSize = (size_t)inSize; + RINOK(inStream->Look((void *)inStream, (void **)&inBuf, &curSize)); + if (curSize == 0) + return SZ_ERROR_INPUT_EOF; + memcpy(outBuffer, inBuf, curSize); + outBuffer += curSize; + inSize -= curSize; + RINOK(inStream->Skip((void *)inStream, curSize)); + } + return SZ_OK; +} + +#define IS_UNSUPPORTED_METHOD(m) ((m) != k_Copy && (m) != k_LZMA) +#define IS_UNSUPPORTED_CODER(c) (IS_UNSUPPORTED_METHOD(c.MethodID) || c.NumInStreams != 1 || c.NumOutStreams != 1) +#define IS_NO_BCJ(c) (c.MethodID != k_BCJ || c.NumInStreams != 1 || c.NumOutStreams != 1) +#define IS_NO_BCJ2(c) (c.MethodID != k_BCJ2 || c.NumInStreams != 4 || c.NumOutStreams != 1) + +SRes CheckSupportedFolder(const CSzFolder *f) +{ + if (f->NumCoders < 1 || f->NumCoders > 4) + return SZ_ERROR_UNSUPPORTED; + if (IS_UNSUPPORTED_CODER(f->Coders[0])) + return SZ_ERROR_UNSUPPORTED; + if (f->NumCoders == 1) + { + if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBindPairs != 0) + return SZ_ERROR_UNSUPPORTED; + return SZ_OK; + } + if (f->NumCoders == 2) + { + if (IS_NO_BCJ(f->Coders[1]) || + f->NumPackStreams != 1 || f->PackStreams[0] != 0 || + f->NumBindPairs != 1 || + f->BindPairs[0].InIndex != 1 || f->BindPairs[0].OutIndex != 0) + return SZ_ERROR_UNSUPPORTED; + return SZ_OK; + } + if (f->NumCoders == 4) + { + if (IS_UNSUPPORTED_CODER(f->Coders[1]) || + IS_UNSUPPORTED_CODER(f->Coders[2]) || + IS_NO_BCJ2(f->Coders[3])) + return SZ_ERROR_UNSUPPORTED; + if (f->NumPackStreams != 4 || + f->PackStreams[0] != 2 || + f->PackStreams[1] != 6 || + f->PackStreams[2] != 1 || + f->PackStreams[3] != 0 || + f->NumBindPairs != 3 || + f->BindPairs[0].InIndex != 5 || f->BindPairs[0].OutIndex != 0 || + f->BindPairs[1].InIndex != 4 || f->BindPairs[1].OutIndex != 1 || + f->BindPairs[2].InIndex != 3 || f->BindPairs[2].OutIndex != 2) + return SZ_ERROR_UNSUPPORTED; + return SZ_OK; + } + return SZ_ERROR_UNSUPPORTED; +} + +UInt64 GetSum(const UInt64 *values, UInt32 index) +{ + UInt64 sum = 0; + UInt32 i; + for (i = 0; i < index; i++) + sum += values[i]; + return sum; +} + +SRes SzDecode2(const UInt64 *packSizes, const CSzFolder *folder, + ILookInStream *inStream, UInt64 startPos, + Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain, + Byte *tempBuf[]) +{ + UInt32 ci; + SizeT tempSizes[3] = { 0, 0, 0}; + SizeT tempSize3 = 0; + Byte *tempBuf3 = 0; + + RINOK(CheckSupportedFolder(folder)); + + for (ci = 0; ci < folder->NumCoders; ci++) + { + CSzCoderInfo *coder = &folder->Coders[ci]; + + if (coder->MethodID == k_Copy || coder->MethodID == k_LZMA) + { + UInt32 si = 0; + UInt64 offset; + UInt64 inSize; + Byte *outBufCur = outBuffer; + SizeT outSizeCur = outSize; + if (folder->NumCoders == 4) + { + UInt32 indices[] = { 3, 2, 0 }; + UInt64 unpackSize = folder->UnpackSizes[ci]; + si = indices[ci]; + if (ci < 2) + { + Byte *temp; + outSizeCur = (SizeT)unpackSize; + if (outSizeCur != unpackSize) + return SZ_ERROR_MEM; + temp = (Byte *)IAlloc_Alloc(allocMain, outSizeCur); + if (temp == 0 && outSizeCur != 0) + return SZ_ERROR_MEM; + outBufCur = tempBuf[1 - ci] = temp; + tempSizes[1 - ci] = outSizeCur; + } + else if (ci == 2) + { + if (unpackSize > outSize) /* check it */ + return SZ_ERROR_PARAM; + tempBuf3 = outBufCur = outBuffer + (outSize - (size_t)unpackSize); + tempSize3 = outSizeCur = (SizeT)unpackSize; + } + else + return SZ_ERROR_UNSUPPORTED; + } + offset = GetSum(packSizes, si); + inSize = packSizes[si]; + RINOK(LookInStream_SeekTo(inStream, startPos + offset)); + + if (coder->MethodID == k_Copy) + { + if (inSize != outSizeCur) /* check it */ + return SZ_ERROR_DATA; + RINOK(SzDecodeCopy(inSize, inStream, outBufCur)); + } + else + { + RINOK(SzDecodeLzma(coder, inSize, inStream, outBufCur, outSizeCur, allocMain)); + } + } + else if (coder->MethodID == k_BCJ) + { + UInt32 state; + if (ci != 1) + return SZ_ERROR_UNSUPPORTED; + x86_Convert_Init(state); + x86_Convert(outBuffer, outSize, 0, &state, 0); + } + else if (coder->MethodID == k_BCJ2) + { + UInt64 offset = GetSum(packSizes, 1); + UInt64 s3Size = packSizes[1]; + SRes res; + if (ci != 3) + return SZ_ERROR_UNSUPPORTED; + RINOK(LookInStream_SeekTo(inStream, startPos + offset)); + tempSizes[2] = (SizeT)s3Size; + if (tempSizes[2] != s3Size) + return SZ_ERROR_MEM; + tempBuf[2] = (Byte *)IAlloc_Alloc(allocMain, tempSizes[2]); + if (tempBuf[2] == 0 && tempSizes[2] != 0) + return SZ_ERROR_MEM; + res = SzDecodeCopy(s3Size, inStream, tempBuf[2]); + RINOK(res) + + res = Bcj2_Decode( + tempBuf3, tempSize3, + tempBuf[0], tempSizes[0], + tempBuf[1], tempSizes[1], + tempBuf[2], tempSizes[2], + outBuffer, outSize); + RINOK(res) + } + else + return SZ_ERROR_UNSUPPORTED; + } + return SZ_OK; +} + +SRes SzDecode(const UInt64 *packSizes, const CSzFolder *folder, + ILookInStream *inStream, UInt64 startPos, + Byte *outBuffer, size_t outSize, ISzAlloc *allocMain) +{ + Byte *tempBuf[3] = { 0, 0, 0}; + int i; + SRes res = SzDecode2(packSizes, folder, inStream, startPos, + outBuffer, (SizeT)outSize, allocMain, tempBuf); + for (i = 0; i < 3; i++) + IAlloc_Free(allocMain, tempBuf[i]); + return res; +} diff --git a/lzma/C/Archive/7z/7zDecode.h b/lzma/C/Archive/7z/7zDecode.h new file mode 100644 index 000000000..9607915bf --- /dev/null +++ b/lzma/C/Archive/7z/7zDecode.h @@ -0,0 +1,13 @@ +/* 7zDecode.h -- Decoding from 7z folder +2008-11-23 : Igor Pavlov : Public domain */ + +#ifndef __7Z_DECODE_H +#define __7Z_DECODE_H + +#include "7zItem.h" + +SRes SzDecode(const UInt64 *packSizes, const CSzFolder *folder, + ILookInStream *stream, UInt64 startPos, + Byte *outBuffer, size_t outSize, ISzAlloc *allocMain); + +#endif diff --git a/lzma/C/Archive/7z/7zExtract.c b/lzma/C/Archive/7z/7zExtract.c new file mode 100644 index 000000000..0f957add1 --- /dev/null +++ b/lzma/C/Archive/7z/7zExtract.c @@ -0,0 +1,93 @@ +/* 7zExtract.c -- Extracting from 7z archive +2008-11-23 : Igor Pavlov : Public domain */ + +#include "../../7zCrc.h" +#include "7zDecode.h" +#include "7zExtract.h" + +SRes SzAr_Extract( + const CSzArEx *p, + ILookInStream *inStream, + UInt32 fileIndex, + UInt32 *blockIndex, + Byte **outBuffer, + size_t *outBufferSize, + size_t *offset, + size_t *outSizeProcessed, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt32 folderIndex = p->FileIndexToFolderIndexMap[fileIndex]; + SRes res = SZ_OK; + *offset = 0; + *outSizeProcessed = 0; + if (folderIndex == (UInt32)-1) + { + IAlloc_Free(allocMain, *outBuffer); + *blockIndex = folderIndex; + *outBuffer = 0; + *outBufferSize = 0; + return SZ_OK; + } + + if (*outBuffer == 0 || *blockIndex != folderIndex) + { + CSzFolder *folder = p->db.Folders + folderIndex; + UInt64 unpackSizeSpec = SzFolder_GetUnpackSize(folder); + size_t unpackSize = (size_t)unpackSizeSpec; + UInt64 startOffset = SzArEx_GetFolderStreamPos(p, folderIndex, 0); + + if (unpackSize != unpackSizeSpec) + return SZ_ERROR_MEM; + *blockIndex = folderIndex; + IAlloc_Free(allocMain, *outBuffer); + *outBuffer = 0; + + RINOK(LookInStream_SeekTo(inStream, startOffset)); + + if (res == SZ_OK) + { + *outBufferSize = unpackSize; + if (unpackSize != 0) + { + *outBuffer = (Byte *)IAlloc_Alloc(allocMain, unpackSize); + if (*outBuffer == 0) + res = SZ_ERROR_MEM; + } + if (res == SZ_OK) + { + res = SzDecode(p->db.PackSizes + + p->FolderStartPackStreamIndex[folderIndex], folder, + inStream, startOffset, + *outBuffer, unpackSize, allocTemp); + if (res == SZ_OK) + { + if (folder->UnpackCRCDefined) + { + if (CrcCalc(*outBuffer, unpackSize) != folder->UnpackCRC) + res = SZ_ERROR_CRC; + } + } + } + } + } + if (res == SZ_OK) + { + UInt32 i; + CSzFileItem *fileItem = p->db.Files + fileIndex; + *offset = 0; + for (i = p->FolderStartFileIndex[folderIndex]; i < fileIndex; i++) + *offset += (UInt32)p->db.Files[i].Size; + *outSizeProcessed = (size_t)fileItem->Size; + if (*offset + *outSizeProcessed > *outBufferSize) + return SZ_ERROR_FAIL; + { + if (fileItem->FileCRCDefined) + { + if (CrcCalc(*outBuffer + *offset, *outSizeProcessed) != fileItem->FileCRC) + res = SZ_ERROR_CRC; + } + } + } + return res; +} diff --git a/lzma/C/Archive/7z/7zExtract.h b/lzma/C/Archive/7z/7zExtract.h new file mode 100644 index 000000000..e171f4ab8 --- /dev/null +++ b/lzma/C/Archive/7z/7zExtract.h @@ -0,0 +1,41 @@ +/* 7zExtract.h -- Extracting from 7z archive +2008-11-23 : Igor Pavlov : Public domain */ + +#ifndef __7Z_EXTRACT_H +#define __7Z_EXTRACT_H + +#include "7zIn.h" + +/* + SzExtract extracts file from archive + + *outBuffer must be 0 before first call for each new archive. + + Extracting cache: + If you need to decompress more than one file, you can send + these values from previous call: + *blockIndex, + *outBuffer, + *outBufferSize + You can consider "*outBuffer" as cache of solid block. If your archive is solid, + it will increase decompression speed. + + If you use external function, you can declare these 3 cache variables + (blockIndex, outBuffer, outBufferSize) as static in that external function. + + Free *outBuffer and set *outBuffer to 0, if you want to flush cache. +*/ + +SRes SzAr_Extract( + const CSzArEx *db, + ILookInStream *inStream, + UInt32 fileIndex, /* index of file */ + UInt32 *blockIndex, /* index of solid block */ + Byte **outBuffer, /* pointer to pointer to output buffer (allocated with allocMain) */ + size_t *outBufferSize, /* buffer size for output buffer */ + size_t *offset, /* offset of stream for required file in *outBuffer */ + size_t *outSizeProcessed, /* size of file in *outBuffer */ + ISzAlloc *allocMain, + ISzAlloc *allocTemp); + +#endif diff --git a/lzma/C/Archive/7z/7zHeader.c b/lzma/C/Archive/7z/7zHeader.c new file mode 100644 index 000000000..c44ff4a48 --- /dev/null +++ b/lzma/C/Archive/7z/7zHeader.c @@ -0,0 +1,6 @@ +/* 7zHeader.c -- 7z Headers +2008-10-04 : Igor Pavlov : Public domain */ + +#include "7zHeader.h" + +Byte k7zSignature[k7zSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; diff --git a/lzma/C/Archive/7z/7zHeader.h b/lzma/C/Archive/7z/7zHeader.h new file mode 100644 index 000000000..1ef8c0196 --- /dev/null +++ b/lzma/C/Archive/7z/7zHeader.h @@ -0,0 +1,57 @@ +/* 7zHeader.h -- 7z Headers +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __7Z_HEADER_H +#define __7Z_HEADER_H + +#include "../../Types.h" + +#define k7zSignatureSize 6 +extern Byte k7zSignature[k7zSignatureSize]; + +#define k7zMajorVersion 0 + +#define k7zStartHeaderSize 0x20 + +enum EIdEnum +{ + k7zIdEnd, + + k7zIdHeader, + + k7zIdArchiveProperties, + + k7zIdAdditionalStreamsInfo, + k7zIdMainStreamsInfo, + k7zIdFilesInfo, + + k7zIdPackInfo, + k7zIdUnpackInfo, + k7zIdSubStreamsInfo, + + k7zIdSize, + k7zIdCRC, + + k7zIdFolder, + + k7zIdCodersUnpackSize, + k7zIdNumUnpackStream, + + k7zIdEmptyStream, + k7zIdEmptyFile, + k7zIdAnti, + + k7zIdName, + k7zIdCTime, + k7zIdATime, + k7zIdMTime, + k7zIdWinAttributes, + k7zIdComment, + + k7zIdEncodedHeader, + + k7zIdStartPos, + k7zIdDummy +}; + +#endif diff --git a/lzma/C/Archive/7z/7zIn.c b/lzma/C/Archive/7z/7zIn.c new file mode 100644 index 000000000..75b0bcd1d --- /dev/null +++ b/lzma/C/Archive/7z/7zIn.c @@ -0,0 +1,1204 @@ +/* 7zIn.c -- 7z Input functions +2008-12-31 : Igor Pavlov : Public domain */ + +#include "../../7zCrc.h" +#include "../../CpuArch.h" + +#include "7zDecode.h" +#include "7zIn.h" + +#define RINOM(x) { if ((x) == 0) return SZ_ERROR_MEM; } + +#define NUM_FOLDER_CODERS_MAX 32 +#define NUM_CODER_STREAMS_MAX 32 + +void SzArEx_Init(CSzArEx *p) +{ + SzAr_Init(&p->db); + p->FolderStartPackStreamIndex = 0; + p->PackStreamStartPositions = 0; + p->FolderStartFileIndex = 0; + p->FileIndexToFolderIndexMap = 0; +} + +void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc) +{ + IAlloc_Free(alloc, p->FolderStartPackStreamIndex); + IAlloc_Free(alloc, p->PackStreamStartPositions); + IAlloc_Free(alloc, p->FolderStartFileIndex); + IAlloc_Free(alloc, p->FileIndexToFolderIndexMap); + SzAr_Free(&p->db, alloc); + SzArEx_Init(p); +} + +/* +UInt64 GetFolderPackStreamSize(int folderIndex, int streamIndex) const +{ + return PackSizes[FolderStartPackStreamIndex[folderIndex] + streamIndex]; +} + +UInt64 GetFilePackSize(int fileIndex) const +{ + int folderIndex = FileIndexToFolderIndexMap[fileIndex]; + if (folderIndex >= 0) + { + const CSzFolder &folderInfo = Folders[folderIndex]; + if (FolderStartFileIndex[folderIndex] == fileIndex) + return GetFolderFullPackSize(folderIndex); + } + return 0; +} +*/ + +#define MY_ALLOC(T, p, size, alloc) { if ((size) == 0) p = 0; else \ + if ((p = (T *)IAlloc_Alloc(alloc, (size) * sizeof(T))) == 0) return SZ_ERROR_MEM; } + +static SRes SzArEx_Fill(CSzArEx *p, ISzAlloc *alloc) +{ + UInt32 startPos = 0; + UInt64 startPosSize = 0; + UInt32 i; + UInt32 folderIndex = 0; + UInt32 indexInFolder = 0; + MY_ALLOC(UInt32, p->FolderStartPackStreamIndex, p->db.NumFolders, alloc); + for (i = 0; i < p->db.NumFolders; i++) + { + p->FolderStartPackStreamIndex[i] = startPos; + startPos += p->db.Folders[i].NumPackStreams; + } + + MY_ALLOC(UInt64, p->PackStreamStartPositions, p->db.NumPackStreams, alloc); + + for (i = 0; i < p->db.NumPackStreams; i++) + { + p->PackStreamStartPositions[i] = startPosSize; + startPosSize += p->db.PackSizes[i]; + } + + MY_ALLOC(UInt32, p->FolderStartFileIndex, p->db.NumFolders, alloc); + MY_ALLOC(UInt32, p->FileIndexToFolderIndexMap, p->db.NumFiles, alloc); + + for (i = 0; i < p->db.NumFiles; i++) + { + CSzFileItem *file = p->db.Files + i; + int emptyStream = !file->HasStream; + if (emptyStream && indexInFolder == 0) + { + p->FileIndexToFolderIndexMap[i] = (UInt32)-1; + continue; + } + if (indexInFolder == 0) + { + /* + v3.13 incorrectly worked with empty folders + v4.07: Loop for skipping empty folders + */ + for (;;) + { + if (folderIndex >= p->db.NumFolders) + return SZ_ERROR_ARCHIVE; + p->FolderStartFileIndex[folderIndex] = i; + if (p->db.Folders[folderIndex].NumUnpackStreams != 0) + break; + folderIndex++; + } + } + p->FileIndexToFolderIndexMap[i] = folderIndex; + if (emptyStream) + continue; + indexInFolder++; + if (indexInFolder >= p->db.Folders[folderIndex].NumUnpackStreams) + { + folderIndex++; + indexInFolder = 0; + } + } + return SZ_OK; +} + + +UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder) +{ + return p->dataPos + + p->PackStreamStartPositions[p->FolderStartPackStreamIndex[folderIndex] + indexInFolder]; +} + +int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize) +{ + UInt32 packStreamIndex = p->FolderStartPackStreamIndex[folderIndex]; + CSzFolder *folder = p->db.Folders + folderIndex; + UInt64 size = 0; + UInt32 i; + for (i = 0; i < folder->NumPackStreams; i++) + { + UInt64 t = size + p->db.PackSizes[packStreamIndex + i]; + if (t < size) /* check it */ + return SZ_ERROR_FAIL; + size = t; + } + *resSize = size; + return SZ_OK; +} + + +/* +SRes SzReadTime(const CObjectVector &dataVector, + CObjectVector &files, UInt64 type) +{ + CBoolVector boolVector; + RINOK(ReadBoolVector2(files.Size(), boolVector)) + + CStreamSwitch streamSwitch; + RINOK(streamSwitch.Set(this, &dataVector)); + + for (int i = 0; i < files.Size(); i++) + { + CSzFileItem &file = files[i]; + CArchiveFileTime fileTime; + bool defined = boolVector[i]; + if (defined) + { + UInt32 low, high; + RINOK(SzReadUInt32(low)); + RINOK(SzReadUInt32(high)); + fileTime.dwLowDateTime = low; + fileTime.dwHighDateTime = high; + } + switch(type) + { + case k7zIdCTime: file.IsCTimeDefined = defined; if (defined) file.CTime = fileTime; break; + case k7zIdATime: file.IsATimeDefined = defined; if (defined) file.ATime = fileTime; break; + case k7zIdMTime: file.IsMTimeDefined = defined; if (defined) file.MTime = fileTime; break; + } + } + return SZ_OK; +} +*/ + +static int TestSignatureCandidate(Byte *testBytes) +{ + size_t i; + for (i = 0; i < k7zSignatureSize; i++) + if (testBytes[i] != k7zSignature[i]) + return 0; + return 1; +} + +typedef struct _CSzState +{ + Byte *Data; + size_t Size; +}CSzData; + +static SRes SzReadByte(CSzData *sd, Byte *b) +{ + if (sd->Size == 0) + return SZ_ERROR_ARCHIVE; + sd->Size--; + *b = *sd->Data++; + return SZ_OK; +} + +static SRes SzReadBytes(CSzData *sd, Byte *data, size_t size) +{ + size_t i; + for (i = 0; i < size; i++) + { + RINOK(SzReadByte(sd, data + i)); + } + return SZ_OK; +} + +static SRes SzReadUInt32(CSzData *sd, UInt32 *value) +{ + int i; + *value = 0; + for (i = 0; i < 4; i++) + { + Byte b; + RINOK(SzReadByte(sd, &b)); + *value |= ((UInt32)(b) << (8 * i)); + } + return SZ_OK; +} + +static SRes SzReadNumber(CSzData *sd, UInt64 *value) +{ + Byte firstByte; + Byte mask = 0x80; + int i; + RINOK(SzReadByte(sd, &firstByte)); + *value = 0; + for (i = 0; i < 8; i++) + { + Byte b; + if ((firstByte & mask) == 0) + { + UInt64 highPart = firstByte & (mask - 1); + *value += (highPart << (8 * i)); + return SZ_OK; + } + RINOK(SzReadByte(sd, &b)); + *value |= ((UInt64)b << (8 * i)); + mask >>= 1; + } + return SZ_OK; +} + +static SRes SzReadNumber32(CSzData *sd, UInt32 *value) +{ + UInt64 value64; + RINOK(SzReadNumber(sd, &value64)); + if (value64 >= 0x80000000) + return SZ_ERROR_UNSUPPORTED; + if (value64 >= ((UInt64)(1) << ((sizeof(size_t) - 1) * 8 + 2))) + return SZ_ERROR_UNSUPPORTED; + *value = (UInt32)value64; + return SZ_OK; +} + +static SRes SzReadID(CSzData *sd, UInt64 *value) +{ + return SzReadNumber(sd, value); +} + +static SRes SzSkeepDataSize(CSzData *sd, UInt64 size) +{ + if (size > sd->Size) + return SZ_ERROR_ARCHIVE; + sd->Size -= (size_t)size; + sd->Data += (size_t)size; + return SZ_OK; +} + +static SRes SzSkeepData(CSzData *sd) +{ + UInt64 size; + RINOK(SzReadNumber(sd, &size)); + return SzSkeepDataSize(sd, size); +} + +static SRes SzReadArchiveProperties(CSzData *sd) +{ + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + break; + SzSkeepData(sd); + } + return SZ_OK; +} + +static SRes SzWaitAttribute(CSzData *sd, UInt64 attribute) +{ + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == attribute) + return SZ_OK; + if (type == k7zIdEnd) + return SZ_ERROR_ARCHIVE; + RINOK(SzSkeepData(sd)); + } +} + +static SRes SzReadBoolVector(CSzData *sd, size_t numItems, Byte **v, ISzAlloc *alloc) +{ + Byte b = 0; + Byte mask = 0; + size_t i; + MY_ALLOC(Byte, *v, numItems, alloc); + for (i = 0; i < numItems; i++) + { + if (mask == 0) + { + RINOK(SzReadByte(sd, &b)); + mask = 0x80; + } + (*v)[i] = (Byte)(((b & mask) != 0) ? 1 : 0); + mask >>= 1; + } + return SZ_OK; +} + +static SRes SzReadBoolVector2(CSzData *sd, size_t numItems, Byte **v, ISzAlloc *alloc) +{ + Byte allAreDefined; + size_t i; + RINOK(SzReadByte(sd, &allAreDefined)); + if (allAreDefined == 0) + return SzReadBoolVector(sd, numItems, v, alloc); + MY_ALLOC(Byte, *v, numItems, alloc); + for (i = 0; i < numItems; i++) + (*v)[i] = 1; + return SZ_OK; +} + +static SRes SzReadHashDigests( + CSzData *sd, + size_t numItems, + Byte **digestsDefined, + UInt32 **digests, + ISzAlloc *alloc) +{ + size_t i; + RINOK(SzReadBoolVector2(sd, numItems, digestsDefined, alloc)); + MY_ALLOC(UInt32, *digests, numItems, alloc); + for (i = 0; i < numItems; i++) + if ((*digestsDefined)[i]) + { + RINOK(SzReadUInt32(sd, (*digests) + i)); + } + return SZ_OK; +} + +static SRes SzReadPackInfo( + CSzData *sd, + UInt64 *dataOffset, + UInt32 *numPackStreams, + UInt64 **packSizes, + Byte **packCRCsDefined, + UInt32 **packCRCs, + ISzAlloc *alloc) +{ + UInt32 i; + RINOK(SzReadNumber(sd, dataOffset)); + RINOK(SzReadNumber32(sd, numPackStreams)); + + RINOK(SzWaitAttribute(sd, k7zIdSize)); + + MY_ALLOC(UInt64, *packSizes, (size_t)*numPackStreams, alloc); + + for (i = 0; i < *numPackStreams; i++) + { + RINOK(SzReadNumber(sd, (*packSizes) + i)); + } + + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + break; + if (type == k7zIdCRC) + { + RINOK(SzReadHashDigests(sd, (size_t)*numPackStreams, packCRCsDefined, packCRCs, alloc)); + continue; + } + RINOK(SzSkeepData(sd)); + } + if (*packCRCsDefined == 0) + { + MY_ALLOC(Byte, *packCRCsDefined, (size_t)*numPackStreams, alloc); + MY_ALLOC(UInt32, *packCRCs, (size_t)*numPackStreams, alloc); + for (i = 0; i < *numPackStreams; i++) + { + (*packCRCsDefined)[i] = 0; + (*packCRCs)[i] = 0; + } + } + return SZ_OK; +} + +static SRes SzReadSwitch(CSzData *sd) +{ + Byte external; + RINOK(SzReadByte(sd, &external)); + return (external == 0) ? SZ_OK: SZ_ERROR_UNSUPPORTED; +} + +static SRes SzGetNextFolderItem(CSzData *sd, CSzFolder *folder, ISzAlloc *alloc) +{ + UInt32 numCoders, numBindPairs, numPackStreams, i; + UInt32 numInStreams = 0, numOutStreams = 0; + + RINOK(SzReadNumber32(sd, &numCoders)); + if (numCoders > NUM_FOLDER_CODERS_MAX) + return SZ_ERROR_UNSUPPORTED; + folder->NumCoders = numCoders; + + MY_ALLOC(CSzCoderInfo, folder->Coders, (size_t)numCoders, alloc); + + for (i = 0; i < numCoders; i++) + SzCoderInfo_Init(folder->Coders + i); + + for (i = 0; i < numCoders; i++) + { + Byte mainByte; + CSzCoderInfo *coder = folder->Coders + i; + { + unsigned idSize, j; + Byte longID[15]; + RINOK(SzReadByte(sd, &mainByte)); + idSize = (unsigned)(mainByte & 0xF); + RINOK(SzReadBytes(sd, longID, idSize)); + if (idSize > sizeof(coder->MethodID)) + return SZ_ERROR_UNSUPPORTED; + coder->MethodID = 0; + for (j = 0; j < idSize; j++) + coder->MethodID |= (UInt64)longID[idSize - 1 - j] << (8 * j); + + if ((mainByte & 0x10) != 0) + { + RINOK(SzReadNumber32(sd, &coder->NumInStreams)); + RINOK(SzReadNumber32(sd, &coder->NumOutStreams)); + if (coder->NumInStreams > NUM_CODER_STREAMS_MAX || + coder->NumOutStreams > NUM_CODER_STREAMS_MAX) + return SZ_ERROR_UNSUPPORTED; + } + else + { + coder->NumInStreams = 1; + coder->NumOutStreams = 1; + } + if ((mainByte & 0x20) != 0) + { + UInt64 propertiesSize = 0; + RINOK(SzReadNumber(sd, &propertiesSize)); + if (!Buf_Create(&coder->Props, (size_t)propertiesSize, alloc)) + return SZ_ERROR_MEM; + RINOK(SzReadBytes(sd, coder->Props.data, (size_t)propertiesSize)); + } + } + while ((mainByte & 0x80) != 0) + { + RINOK(SzReadByte(sd, &mainByte)); + RINOK(SzSkeepDataSize(sd, (mainByte & 0xF))); + if ((mainByte & 0x10) != 0) + { + UInt32 n; + RINOK(SzReadNumber32(sd, &n)); + RINOK(SzReadNumber32(sd, &n)); + } + if ((mainByte & 0x20) != 0) + { + UInt64 propertiesSize = 0; + RINOK(SzReadNumber(sd, &propertiesSize)); + RINOK(SzSkeepDataSize(sd, propertiesSize)); + } + } + numInStreams += coder->NumInStreams; + numOutStreams += coder->NumOutStreams; + } + + if (numOutStreams == 0) + return SZ_ERROR_UNSUPPORTED; + + folder->NumBindPairs = numBindPairs = numOutStreams - 1; + MY_ALLOC(CBindPair, folder->BindPairs, (size_t)numBindPairs, alloc); + + for (i = 0; i < numBindPairs; i++) + { + CBindPair *bp = folder->BindPairs + i; + RINOK(SzReadNumber32(sd, &bp->InIndex)); + RINOK(SzReadNumber32(sd, &bp->OutIndex)); + } + + if (numInStreams < numBindPairs) + return SZ_ERROR_UNSUPPORTED; + + folder->NumPackStreams = numPackStreams = numInStreams - numBindPairs; + MY_ALLOC(UInt32, folder->PackStreams, (size_t)numPackStreams, alloc); + + if (numPackStreams == 1) + { + for (i = 0; i < numInStreams ; i++) + if (SzFolder_FindBindPairForInStream(folder, i) < 0) + break; + if (i == numInStreams) + return SZ_ERROR_UNSUPPORTED; + folder->PackStreams[0] = i; + } + else + for (i = 0; i < numPackStreams; i++) + { + RINOK(SzReadNumber32(sd, folder->PackStreams + i)); + } + return SZ_OK; +} + +static SRes SzReadUnpackInfo( + CSzData *sd, + UInt32 *numFolders, + CSzFolder **folders, /* for alloc */ + ISzAlloc *alloc, + ISzAlloc *allocTemp) +{ + UInt32 i; + RINOK(SzWaitAttribute(sd, k7zIdFolder)); + RINOK(SzReadNumber32(sd, numFolders)); + { + RINOK(SzReadSwitch(sd)); + + MY_ALLOC(CSzFolder, *folders, (size_t)*numFolders, alloc); + + for (i = 0; i < *numFolders; i++) + SzFolder_Init((*folders) + i); + + for (i = 0; i < *numFolders; i++) + { + RINOK(SzGetNextFolderItem(sd, (*folders) + i, alloc)); + } + } + + RINOK(SzWaitAttribute(sd, k7zIdCodersUnpackSize)); + + for (i = 0; i < *numFolders; i++) + { + UInt32 j; + CSzFolder *folder = (*folders) + i; + UInt32 numOutStreams = SzFolder_GetNumOutStreams(folder); + + MY_ALLOC(UInt64, folder->UnpackSizes, (size_t)numOutStreams, alloc); + + for (j = 0; j < numOutStreams; j++) + { + RINOK(SzReadNumber(sd, folder->UnpackSizes + j)); + } + } + + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + return SZ_OK; + if (type == k7zIdCRC) + { + SRes res; + Byte *crcsDefined = 0; + UInt32 *crcs = 0; + res = SzReadHashDigests(sd, *numFolders, &crcsDefined, &crcs, allocTemp); + if (res == SZ_OK) + { + for (i = 0; i < *numFolders; i++) + { + CSzFolder *folder = (*folders) + i; + folder->UnpackCRCDefined = crcsDefined[i]; + folder->UnpackCRC = crcs[i]; + } + } + IAlloc_Free(allocTemp, crcs); + IAlloc_Free(allocTemp, crcsDefined); + RINOK(res); + continue; + } + RINOK(SzSkeepData(sd)); + } +} + +static SRes SzReadSubStreamsInfo( + CSzData *sd, + UInt32 numFolders, + CSzFolder *folders, + UInt32 *numUnpackStreams, + UInt64 **unpackSizes, + Byte **digestsDefined, + UInt32 **digests, + ISzAlloc *allocTemp) +{ + UInt64 type = 0; + UInt32 i; + UInt32 si = 0; + UInt32 numDigests = 0; + + for (i = 0; i < numFolders; i++) + folders[i].NumUnpackStreams = 1; + *numUnpackStreams = numFolders; + + for (;;) + { + RINOK(SzReadID(sd, &type)); + if (type == k7zIdNumUnpackStream) + { + *numUnpackStreams = 0; + for (i = 0; i < numFolders; i++) + { + UInt32 numStreams; + RINOK(SzReadNumber32(sd, &numStreams)); + folders[i].NumUnpackStreams = numStreams; + *numUnpackStreams += numStreams; + } + continue; + } + if (type == k7zIdCRC || type == k7zIdSize) + break; + if (type == k7zIdEnd) + break; + RINOK(SzSkeepData(sd)); + } + + if (*numUnpackStreams == 0) + { + *unpackSizes = 0; + *digestsDefined = 0; + *digests = 0; + } + else + { + *unpackSizes = (UInt64 *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(UInt64)); + RINOM(*unpackSizes); + *digestsDefined = (Byte *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(Byte)); + RINOM(*digestsDefined); + *digests = (UInt32 *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(UInt32)); + RINOM(*digests); + } + + for (i = 0; i < numFolders; i++) + { + /* + v3.13 incorrectly worked with empty folders + v4.07: we check that folder is empty + */ + UInt64 sum = 0; + UInt32 j; + UInt32 numSubstreams = folders[i].NumUnpackStreams; + if (numSubstreams == 0) + continue; + if (type == k7zIdSize) + for (j = 1; j < numSubstreams; j++) + { + UInt64 size; + RINOK(SzReadNumber(sd, &size)); + (*unpackSizes)[si++] = size; + sum += size; + } + (*unpackSizes)[si++] = SzFolder_GetUnpackSize(folders + i) - sum; + } + if (type == k7zIdSize) + { + RINOK(SzReadID(sd, &type)); + } + + for (i = 0; i < *numUnpackStreams; i++) + { + (*digestsDefined)[i] = 0; + (*digests)[i] = 0; + } + + + for (i = 0; i < numFolders; i++) + { + UInt32 numSubstreams = folders[i].NumUnpackStreams; + if (numSubstreams != 1 || !folders[i].UnpackCRCDefined) + numDigests += numSubstreams; + } + + + si = 0; + for (;;) + { + if (type == k7zIdCRC) + { + int digestIndex = 0; + Byte *digestsDefined2 = 0; + UInt32 *digests2 = 0; + SRes res = SzReadHashDigests(sd, numDigests, &digestsDefined2, &digests2, allocTemp); + if (res == SZ_OK) + { + for (i = 0; i < numFolders; i++) + { + CSzFolder *folder = folders + i; + UInt32 numSubstreams = folder->NumUnpackStreams; + if (numSubstreams == 1 && folder->UnpackCRCDefined) + { + (*digestsDefined)[si] = 1; + (*digests)[si] = folder->UnpackCRC; + si++; + } + else + { + UInt32 j; + for (j = 0; j < numSubstreams; j++, digestIndex++) + { + (*digestsDefined)[si] = digestsDefined2[digestIndex]; + (*digests)[si] = digests2[digestIndex]; + si++; + } + } + } + } + IAlloc_Free(allocTemp, digestsDefined2); + IAlloc_Free(allocTemp, digests2); + RINOK(res); + } + else if (type == k7zIdEnd) + return SZ_OK; + else + { + RINOK(SzSkeepData(sd)); + } + RINOK(SzReadID(sd, &type)); + } +} + + +static SRes SzReadStreamsInfo( + CSzData *sd, + UInt64 *dataOffset, + CSzAr *p, + UInt32 *numUnpackStreams, + UInt64 **unpackSizes, /* allocTemp */ + Byte **digestsDefined, /* allocTemp */ + UInt32 **digests, /* allocTemp */ + ISzAlloc *alloc, + ISzAlloc *allocTemp) +{ + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if ((UInt64)(int)type != type) + return SZ_ERROR_UNSUPPORTED; + switch((int)type) + { + case k7zIdEnd: + return SZ_OK; + case k7zIdPackInfo: + { + RINOK(SzReadPackInfo(sd, dataOffset, &p->NumPackStreams, + &p->PackSizes, &p->PackCRCsDefined, &p->PackCRCs, alloc)); + break; + } + case k7zIdUnpackInfo: + { + RINOK(SzReadUnpackInfo(sd, &p->NumFolders, &p->Folders, alloc, allocTemp)); + break; + } + case k7zIdSubStreamsInfo: + { + RINOK(SzReadSubStreamsInfo(sd, p->NumFolders, p->Folders, + numUnpackStreams, unpackSizes, digestsDefined, digests, allocTemp)); + break; + } + default: + return SZ_ERROR_UNSUPPORTED; + } + } +} + +Byte kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +static SRes SzReadFileNames(CSzData *sd, UInt32 numFiles, CSzFileItem *files, ISzAlloc *alloc) +{ + UInt32 i; + for (i = 0; i < numFiles; i++) + { + UInt32 len = 0; + UInt32 pos = 0; + CSzFileItem *file = files + i; + while (pos + 2 <= sd->Size) + { + int numAdds; + UInt32 value = (UInt32)(sd->Data[pos] | (((UInt32)sd->Data[pos + 1]) << 8)); + pos += 2; + len++; + if (value == 0) + break; + if (value < 0x80) + continue; + if (value >= 0xD800 && value < 0xE000) + { + UInt32 c2; + if (value >= 0xDC00) + return SZ_ERROR_ARCHIVE; + if (pos + 2 > sd->Size) + return SZ_ERROR_ARCHIVE; + c2 = (UInt32)(sd->Data[pos] | (((UInt32)sd->Data[pos + 1]) << 8)); + pos += 2; + if (c2 < 0xDC00 || c2 >= 0xE000) + return SZ_ERROR_ARCHIVE; + value = ((value - 0xD800) << 10) | (c2 - 0xDC00); + } + for (numAdds = 1; numAdds < 5; numAdds++) + if (value < (((UInt32)1) << (numAdds * 5 + 6))) + break; + len += numAdds; + } + + MY_ALLOC(char, file->Name, (size_t)len, alloc); + + len = 0; + while (2 <= sd->Size) + { + int numAdds; + UInt32 value = (UInt32)(sd->Data[0] | (((UInt32)sd->Data[1]) << 8)); + SzSkeepDataSize(sd, 2); + if (value < 0x80) + { + file->Name[len++] = (char)value; + if (value == 0) + break; + continue; + } + if (value >= 0xD800 && value < 0xE000) + { + UInt32 c2 = (UInt32)(sd->Data[0] | (((UInt32)sd->Data[1]) << 8)); + SzSkeepDataSize(sd, 2); + value = ((value - 0xD800) << 10) | (c2 - 0xDC00); + } + for (numAdds = 1; numAdds < 5; numAdds++) + if (value < (((UInt32)1) << (numAdds * 5 + 6))) + break; + file->Name[len++] = (char)(kUtf8Limits[numAdds - 1] + (value >> (6 * numAdds))); + do + { + numAdds--; + file->Name[len++] = (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F)); + } + while (numAdds > 0); + + len += numAdds; + } + } + return SZ_OK; +} + +static SRes SzReadHeader2( + CSzArEx *p, /* allocMain */ + CSzData *sd, + UInt64 **unpackSizes, /* allocTemp */ + Byte **digestsDefined, /* allocTemp */ + UInt32 **digests, /* allocTemp */ + Byte **emptyStreamVector, /* allocTemp */ + Byte **emptyFileVector, /* allocTemp */ + Byte **lwtVector, /* allocTemp */ + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt64 type; + UInt32 numUnpackStreams = 0; + UInt32 numFiles = 0; + CSzFileItem *files = 0; + UInt32 numEmptyStreams = 0; + UInt32 i; + + RINOK(SzReadID(sd, &type)); + + if (type == k7zIdArchiveProperties) + { + RINOK(SzReadArchiveProperties(sd)); + RINOK(SzReadID(sd, &type)); + } + + + if (type == k7zIdMainStreamsInfo) + { + RINOK(SzReadStreamsInfo(sd, + &p->dataPos, + &p->db, + &numUnpackStreams, + unpackSizes, + digestsDefined, + digests, allocMain, allocTemp)); + p->dataPos += p->startPosAfterHeader; + RINOK(SzReadID(sd, &type)); + } + + if (type == k7zIdEnd) + return SZ_OK; + if (type != k7zIdFilesInfo) + return SZ_ERROR_ARCHIVE; + + RINOK(SzReadNumber32(sd, &numFiles)); + p->db.NumFiles = numFiles; + + MY_ALLOC(CSzFileItem, files, (size_t)numFiles, allocMain); + + p->db.Files = files; + for (i = 0; i < numFiles; i++) + SzFile_Init(files + i); + + for (;;) + { + UInt64 type; + UInt64 size; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + break; + RINOK(SzReadNumber(sd, &size)); + + if ((UInt64)(int)type != type) + { + RINOK(SzSkeepDataSize(sd, size)); + } + else + switch((int)type) + { + case k7zIdName: + { + RINOK(SzReadSwitch(sd)); + RINOK(SzReadFileNames(sd, numFiles, files, allocMain)) + break; + } + case k7zIdEmptyStream: + { + RINOK(SzReadBoolVector(sd, numFiles, emptyStreamVector, allocTemp)); + numEmptyStreams = 0; + for (i = 0; i < numFiles; i++) + if ((*emptyStreamVector)[i]) + numEmptyStreams++; + break; + } + case k7zIdEmptyFile: + { + RINOK(SzReadBoolVector(sd, numEmptyStreams, emptyFileVector, allocTemp)); + break; + } + case k7zIdMTime: + { + RINOK(SzReadBoolVector2(sd, numFiles, lwtVector, allocTemp)); + RINOK(SzReadSwitch(sd)); + for (i = 0; i < numFiles; i++) + { + CSzFileItem *f = &files[i]; + Byte defined = (*lwtVector)[i]; + f->MTimeDefined = defined; + f->MTime.Low = f->MTime.High = 0; + if (defined) + { + RINOK(SzReadUInt32(sd, &f->MTime.Low)); + RINOK(SzReadUInt32(sd, &f->MTime.High)); + } + } + break; + } + default: + { + RINOK(SzSkeepDataSize(sd, size)); + } + } + } + + { + UInt32 emptyFileIndex = 0; + UInt32 sizeIndex = 0; + for (i = 0; i < numFiles; i++) + { + CSzFileItem *file = files + i; + file->IsAnti = 0; + if (*emptyStreamVector == 0) + file->HasStream = 1; + else + file->HasStream = (Byte)((*emptyStreamVector)[i] ? 0 : 1); + if (file->HasStream) + { + file->IsDir = 0; + file->Size = (*unpackSizes)[sizeIndex]; + file->FileCRC = (*digests)[sizeIndex]; + file->FileCRCDefined = (Byte)(*digestsDefined)[sizeIndex]; + sizeIndex++; + } + else + { + if (*emptyFileVector == 0) + file->IsDir = 1; + else + file->IsDir = (Byte)((*emptyFileVector)[emptyFileIndex] ? 0 : 1); + emptyFileIndex++; + file->Size = 0; + file->FileCRCDefined = 0; + } + } + } + return SzArEx_Fill(p, allocMain); +} + +static SRes SzReadHeader( + CSzArEx *p, + CSzData *sd, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt64 *unpackSizes = 0; + Byte *digestsDefined = 0; + UInt32 *digests = 0; + Byte *emptyStreamVector = 0; + Byte *emptyFileVector = 0; + Byte *lwtVector = 0; + SRes res = SzReadHeader2(p, sd, + &unpackSizes, &digestsDefined, &digests, + &emptyStreamVector, &emptyFileVector, &lwtVector, + allocMain, allocTemp); + IAlloc_Free(allocTemp, unpackSizes); + IAlloc_Free(allocTemp, digestsDefined); + IAlloc_Free(allocTemp, digests); + IAlloc_Free(allocTemp, emptyStreamVector); + IAlloc_Free(allocTemp, emptyFileVector); + IAlloc_Free(allocTemp, lwtVector); + return res; +} + +static SRes SzReadAndDecodePackedStreams2( + ILookInStream *inStream, + CSzData *sd, + CBuf *outBuffer, + UInt64 baseOffset, + CSzAr *p, + UInt64 **unpackSizes, + Byte **digestsDefined, + UInt32 **digests, + ISzAlloc *allocTemp) +{ + + UInt32 numUnpackStreams = 0; + UInt64 dataStartPos; + CSzFolder *folder; + UInt64 unpackSize; + SRes res; + + RINOK(SzReadStreamsInfo(sd, &dataStartPos, p, + &numUnpackStreams, unpackSizes, digestsDefined, digests, + allocTemp, allocTemp)); + + dataStartPos += baseOffset; + if (p->NumFolders != 1) + return SZ_ERROR_ARCHIVE; + + folder = p->Folders; + unpackSize = SzFolder_GetUnpackSize(folder); + + RINOK(LookInStream_SeekTo(inStream, dataStartPos)); + + if (!Buf_Create(outBuffer, (size_t)unpackSize, allocTemp)) + return SZ_ERROR_MEM; + + res = SzDecode(p->PackSizes, folder, + inStream, dataStartPos, + outBuffer->data, (size_t)unpackSize, allocTemp); + RINOK(res); + if (folder->UnpackCRCDefined) + if (CrcCalc(outBuffer->data, (size_t)unpackSize) != folder->UnpackCRC) + return SZ_ERROR_CRC; + return SZ_OK; +} + +static SRes SzReadAndDecodePackedStreams( + ILookInStream *inStream, + CSzData *sd, + CBuf *outBuffer, + UInt64 baseOffset, + ISzAlloc *allocTemp) +{ + CSzAr p; + UInt64 *unpackSizes = 0; + Byte *digestsDefined = 0; + UInt32 *digests = 0; + SRes res; + SzAr_Init(&p); + res = SzReadAndDecodePackedStreams2(inStream, sd, outBuffer, baseOffset, + &p, &unpackSizes, &digestsDefined, &digests, + allocTemp); + SzAr_Free(&p, allocTemp); + IAlloc_Free(allocTemp, unpackSizes); + IAlloc_Free(allocTemp, digestsDefined); + IAlloc_Free(allocTemp, digests); + return res; +} + +static SRes SzArEx_Open2( + CSzArEx *p, + ILookInStream *inStream, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + Byte header[k7zStartHeaderSize]; + UInt64 nextHeaderOffset, nextHeaderSize; + size_t nextHeaderSizeT; + UInt32 nextHeaderCRC; + CBuf buffer; + SRes res; + + RINOK(LookInStream_Read2(inStream, header, k7zStartHeaderSize, SZ_ERROR_NO_ARCHIVE)); + + if (!TestSignatureCandidate(header)) + return SZ_ERROR_NO_ARCHIVE; + if (header[6] != k7zMajorVersion) + return SZ_ERROR_UNSUPPORTED; + + nextHeaderOffset = GetUi64(header + 12); + nextHeaderSize = GetUi64(header + 20); + nextHeaderCRC = GetUi32(header + 28); + + p->startPosAfterHeader = k7zStartHeaderSize; + + if (CrcCalc(header + 12, 20) != GetUi32(header + 8)) + return SZ_ERROR_CRC; + + nextHeaderSizeT = (size_t)nextHeaderSize; + if (nextHeaderSizeT != nextHeaderSize) + return SZ_ERROR_MEM; + if (nextHeaderSizeT == 0) + return SZ_OK; + if (nextHeaderOffset > nextHeaderOffset + nextHeaderSize || + nextHeaderOffset > nextHeaderOffset + nextHeaderSize + k7zStartHeaderSize) + return SZ_ERROR_NO_ARCHIVE; + + { + Int64 pos = 0; + RINOK(inStream->Seek(inStream, &pos, SZ_SEEK_END)); + if ((UInt64)pos < nextHeaderOffset || + (UInt64)pos < k7zStartHeaderSize + nextHeaderOffset || + (UInt64)pos < k7zStartHeaderSize + nextHeaderOffset + nextHeaderSize) + return SZ_ERROR_INPUT_EOF; + } + + RINOK(LookInStream_SeekTo(inStream, k7zStartHeaderSize + nextHeaderOffset)); + + if (!Buf_Create(&buffer, nextHeaderSizeT, allocTemp)) + return SZ_ERROR_MEM; + + res = LookInStream_Read(inStream, buffer.data, nextHeaderSizeT); + if (res == SZ_OK) + { + res = SZ_ERROR_ARCHIVE; + if (CrcCalc(buffer.data, nextHeaderSizeT) == nextHeaderCRC) + { + CSzData sd; + UInt64 type; + sd.Data = buffer.data; + sd.Size = buffer.size; + res = SzReadID(&sd, &type); + if (res == SZ_OK) + { + if (type == k7zIdEncodedHeader) + { + CBuf outBuffer; + Buf_Init(&outBuffer); + res = SzReadAndDecodePackedStreams(inStream, &sd, &outBuffer, p->startPosAfterHeader, allocTemp); + if (res != SZ_OK) + Buf_Free(&outBuffer, allocTemp); + else + { + Buf_Free(&buffer, allocTemp); + buffer.data = outBuffer.data; + buffer.size = outBuffer.size; + sd.Data = buffer.data; + sd.Size = buffer.size; + res = SzReadID(&sd, &type); + } + } + } + if (res == SZ_OK) + { + if (type == k7zIdHeader) + res = SzReadHeader(p, &sd, allocMain, allocTemp); + else + res = SZ_ERROR_UNSUPPORTED; + } + } + } + Buf_Free(&buffer, allocTemp); + return res; +} + +SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp) +{ + SRes res = SzArEx_Open2(p, inStream, allocMain, allocTemp); + if (res != SZ_OK) + SzArEx_Free(p, allocMain); + return res; +} diff --git a/lzma/C/Archive/7z/7zIn.h b/lzma/C/Archive/7z/7zIn.h new file mode 100644 index 000000000..717fe52c9 --- /dev/null +++ b/lzma/C/Archive/7z/7zIn.h @@ -0,0 +1,41 @@ +/* 7zIn.h -- 7z Input functions +2008-11-23 : Igor Pavlov : Public domain */ + +#ifndef __7Z_IN_H +#define __7Z_IN_H + +#include "7zHeader.h" +#include "7zItem.h" + +typedef struct +{ + CSzAr db; + + UInt64 startPosAfterHeader; + UInt64 dataPos; + + UInt32 *FolderStartPackStreamIndex; + UInt64 *PackStreamStartPositions; + UInt32 *FolderStartFileIndex; + UInt32 *FileIndexToFolderIndexMap; +} CSzArEx; + +void SzArEx_Init(CSzArEx *p); +void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc); +UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder); +int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize); + +/* +Errors: +SZ_ERROR_NO_ARCHIVE +SZ_ERROR_ARCHIVE +SZ_ERROR_UNSUPPORTED +SZ_ERROR_MEM +SZ_ERROR_CRC +SZ_ERROR_INPUT_EOF +SZ_ERROR_FAIL +*/ + +SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp); + +#endif diff --git a/lzma/C/Archive/7z/7zItem.c b/lzma/C/Archive/7z/7zItem.c new file mode 100644 index 000000000..a11d5008f --- /dev/null +++ b/lzma/C/Archive/7z/7zItem.c @@ -0,0 +1,127 @@ +/* 7zItem.c -- 7z Items +2008-10-04 : Igor Pavlov : Public domain */ + +#include "7zItem.h" + +void SzCoderInfo_Init(CSzCoderInfo *p) +{ + Buf_Init(&p->Props); +} + +void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc) +{ + Buf_Free(&p->Props, alloc); + SzCoderInfo_Init(p); +} + +void SzFolder_Init(CSzFolder *p) +{ + p->Coders = 0; + p->BindPairs = 0; + p->PackStreams = 0; + p->UnpackSizes = 0; + p->NumCoders = 0; + p->NumBindPairs = 0; + p->NumPackStreams = 0; + p->UnpackCRCDefined = 0; + p->UnpackCRC = 0; + p->NumUnpackStreams = 0; +} + +void SzFolder_Free(CSzFolder *p, ISzAlloc *alloc) +{ + UInt32 i; + if (p->Coders) + for (i = 0; i < p->NumCoders; i++) + SzCoderInfo_Free(&p->Coders[i], alloc); + IAlloc_Free(alloc, p->Coders); + IAlloc_Free(alloc, p->BindPairs); + IAlloc_Free(alloc, p->PackStreams); + IAlloc_Free(alloc, p->UnpackSizes); + SzFolder_Init(p); +} + +UInt32 SzFolder_GetNumOutStreams(CSzFolder *p) +{ + UInt32 result = 0; + UInt32 i; + for (i = 0; i < p->NumCoders; i++) + result += p->Coders[i].NumOutStreams; + return result; +} + +int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex) +{ + UInt32 i; + for (i = 0; i < p->NumBindPairs; i++) + if (p->BindPairs[i].InIndex == inStreamIndex) + return i; + return -1; +} + + +int SzFolder_FindBindPairForOutStream(CSzFolder *p, UInt32 outStreamIndex) +{ + UInt32 i; + for (i = 0; i < p->NumBindPairs; i++) + if (p->BindPairs[i].OutIndex == outStreamIndex) + return i; + return -1; +} + +UInt64 SzFolder_GetUnpackSize(CSzFolder *p) +{ + int i = (int)SzFolder_GetNumOutStreams(p); + if (i == 0) + return 0; + for (i--; i >= 0; i--) + if (SzFolder_FindBindPairForOutStream(p, i) < 0) + return p->UnpackSizes[i]; + /* throw 1; */ + return 0; +} + +void SzFile_Init(CSzFileItem *p) +{ + p->HasStream = 1; + p->IsDir = 0; + p->IsAnti = 0; + p->FileCRCDefined = 0; + p->MTimeDefined = 0; + p->Name = 0; +} + +static void SzFile_Free(CSzFileItem *p, ISzAlloc *alloc) +{ + IAlloc_Free(alloc, p->Name); + SzFile_Init(p); +} + +void SzAr_Init(CSzAr *p) +{ + p->PackSizes = 0; + p->PackCRCsDefined = 0; + p->PackCRCs = 0; + p->Folders = 0; + p->Files = 0; + p->NumPackStreams = 0; + p->NumFolders = 0; + p->NumFiles = 0; +} + +void SzAr_Free(CSzAr *p, ISzAlloc *alloc) +{ + UInt32 i; + if (p->Folders) + for (i = 0; i < p->NumFolders; i++) + SzFolder_Free(&p->Folders[i], alloc); + if (p->Files) + for (i = 0; i < p->NumFiles; i++) + SzFile_Free(&p->Files[i], alloc); + IAlloc_Free(alloc, p->PackSizes); + IAlloc_Free(alloc, p->PackCRCsDefined); + IAlloc_Free(alloc, p->PackCRCs); + IAlloc_Free(alloc, p->Folders); + IAlloc_Free(alloc, p->Files); + SzAr_Init(p); +} diff --git a/lzma/C/Archive/7z/7zItem.h b/lzma/C/Archive/7z/7zItem.h new file mode 100644 index 000000000..190b2e2a9 --- /dev/null +++ b/lzma/C/Archive/7z/7zItem.h @@ -0,0 +1,84 @@ +/* 7zItem.h -- 7z Items +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __7Z_ITEM_H +#define __7Z_ITEM_H + +#include "../../7zBuf.h" + +typedef struct +{ + UInt32 NumInStreams; + UInt32 NumOutStreams; + UInt64 MethodID; + CBuf Props; +} CSzCoderInfo; + +void SzCoderInfo_Init(CSzCoderInfo *p); +void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc); + +typedef struct +{ + UInt32 InIndex; + UInt32 OutIndex; +} CBindPair; + +typedef struct +{ + CSzCoderInfo *Coders; + CBindPair *BindPairs; + UInt32 *PackStreams; + UInt64 *UnpackSizes; + UInt32 NumCoders; + UInt32 NumBindPairs; + UInt32 NumPackStreams; + int UnpackCRCDefined; + UInt32 UnpackCRC; + + UInt32 NumUnpackStreams; +} CSzFolder; + +void SzFolder_Init(CSzFolder *p); +UInt64 SzFolder_GetUnpackSize(CSzFolder *p); +int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex); +UInt32 SzFolder_GetNumOutStreams(CSzFolder *p); +UInt64 SzFolder_GetUnpackSize(CSzFolder *p); + +typedef struct CNtfsFileTime +{ + UInt32 Low; + UInt32 High; +} CNtfsFileTime; + +typedef struct CSzFileItem +{ + CNtfsFileTime MTime; + UInt64 Size; + char *Name; + UInt32 FileCRC; + + Byte HasStream; + Byte IsDir; + Byte IsAnti; + Byte FileCRCDefined; + Byte MTimeDefined; +} CSzFileItem; + +void SzFile_Init(CSzFileItem *p); + +typedef struct CSzAr +{ + UInt64 *PackSizes; + Byte *PackCRCsDefined; + UInt32 *PackCRCs; + CSzFolder *Folders; + CSzFileItem *Files; + UInt32 NumPackStreams; + UInt32 NumFolders; + UInt32 NumFiles; +} CSzAr; + +void SzAr_Init(CSzAr *p); +void SzAr_Free(CSzAr *p, ISzAlloc *alloc); + +#endif diff --git a/lzma/C/Bcj2.c b/lzma/C/Bcj2.c new file mode 100644 index 000000000..474bdd45d --- /dev/null +++ b/lzma/C/Bcj2.c @@ -0,0 +1,132 @@ +/* Bcj2.c -- Converter for x86 code (BCJ2) +2008-10-04 : Igor Pavlov : Public domain */ + +#include "Bcj2.h" + +#ifdef _LZMA_PROB32 +#define CProb UInt32 +#else +#define CProb UInt16 +#endif + +#define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80) +#define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)) + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_READ_BYTE (*buffer++) +#define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; } +#define RC_INIT2 code = 0; range = 0xFFFFFFFF; \ + { int i; for (i = 0; i < 5; i++) { RC_TEST; code = (code << 8) | RC_READ_BYTE; }} + +#define NORMALIZE if (range < kTopValue) { RC_TEST; range <<= 8; code = (code << 8) | RC_READ_BYTE; } + +#define IF_BIT_0(p) ttt = *(p); bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0(p) range = bound; *(p) = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); NORMALIZE; +#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CProb)(ttt - (ttt >> kNumMoveBits)); NORMALIZE; + +int Bcj2_Decode( + const Byte *buf0, SizeT size0, + const Byte *buf1, SizeT size1, + const Byte *buf2, SizeT size2, + const Byte *buf3, SizeT size3, + Byte *outBuf, SizeT outSize) +{ + CProb p[256 + 2]; + SizeT inPos = 0, outPos = 0; + + const Byte *buffer, *bufferLim; + UInt32 range, code; + Byte prevByte = 0; + + unsigned int i; + for (i = 0; i < sizeof(p) / sizeof(p[0]); i++) + p[i] = kBitModelTotal >> 1; + + buffer = buf3; + bufferLim = buffer + size3; + RC_INIT2 + + if (outSize == 0) + return SZ_OK; + + for (;;) + { + Byte b; + CProb *prob; + UInt32 bound; + UInt32 ttt; + + SizeT limit = size0 - inPos; + if (outSize - outPos < limit) + limit = outSize - outPos; + while (limit != 0) + { + Byte b = buf0[inPos]; + outBuf[outPos++] = b; + if (IsJ(prevByte, b)) + break; + inPos++; + prevByte = b; + limit--; + } + + if (limit == 0 || outPos == outSize) + break; + + b = buf0[inPos++]; + + if (b == 0xE8) + prob = p + prevByte; + else if (b == 0xE9) + prob = p + 256; + else + prob = p + 257; + + IF_BIT_0(prob) + { + UPDATE_0(prob) + prevByte = b; + } + else + { + UInt32 dest; + const Byte *v; + UPDATE_1(prob) + if (b == 0xE8) + { + v = buf1; + if (size1 < 4) + return SZ_ERROR_DATA; + buf1 += 4; + size1 -= 4; + } + else + { + v = buf2; + if (size2 < 4) + return SZ_ERROR_DATA; + buf2 += 4; + size2 -= 4; + } + dest = (((UInt32)v[0] << 24) | ((UInt32)v[1] << 16) | + ((UInt32)v[2] << 8) | ((UInt32)v[3])) - ((UInt32)outPos + 4); + outBuf[outPos++] = (Byte)dest; + if (outPos == outSize) + break; + outBuf[outPos++] = (Byte)(dest >> 8); + if (outPos == outSize) + break; + outBuf[outPos++] = (Byte)(dest >> 16); + if (outPos == outSize) + break; + outBuf[outPos++] = prevByte = (Byte)(dest >> 24); + } + } + return (outPos == outSize) ? SZ_OK : SZ_ERROR_DATA; +} diff --git a/lzma/C/Bcj2.h b/lzma/C/Bcj2.h new file mode 100644 index 000000000..f58e3bcb4 --- /dev/null +++ b/lzma/C/Bcj2.h @@ -0,0 +1,30 @@ +/* Bcj2.h -- Converter for x86 code (BCJ2) +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __BCJ2_H +#define __BCJ2_H + +#include "Types.h" + +/* +Conditions: + outSize <= FullOutputSize, + where FullOutputSize is full size of output stream of x86_2 filter. + +If buf0 overlaps outBuf, there are two required conditions: + 1) (buf0 >= outBuf) + 2) (buf0 + size0 >= outBuf + FullOutputSize). + +Returns: + SZ_OK + SZ_ERROR_DATA - Data error +*/ + +int Bcj2_Decode( + const Byte *buf0, SizeT size0, + const Byte *buf1, SizeT size1, + const Byte *buf2, SizeT size2, + const Byte *buf3, SizeT size3, + Byte *outBuf, SizeT outSize); + +#endif diff --git a/lzma/C/Bra.h b/lzma/C/Bra.h new file mode 100644 index 000000000..b9018eb99 --- /dev/null +++ b/lzma/C/Bra.h @@ -0,0 +1,60 @@ +/* Bra.h -- Branch converters for executables +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __BRA_H +#define __BRA_H + +#include "Types.h" + +/* +These functions convert relative addresses to absolute addresses +in CALL instructions to increase the compression ratio. + + In: + data - data buffer + size - size of data + ip - current virtual Instruction Pinter (IP) value + state - state variable for x86 converter + encoding - 0 (for decoding), 1 (for encoding) + + Out: + state - state variable for x86 converter + + Returns: + The number of processed bytes. If you call these functions with multiple calls, + you must start next call with first byte after block of processed bytes. + + Type Endian Alignment LookAhead + + x86 little 1 4 + ARMT little 2 2 + ARM little 4 0 + PPC big 4 0 + SPARC big 4 0 + IA64 little 16 0 + + size must be >= Alignment + LookAhead, if it's not last block. + If (size < Alignment + LookAhead), converter returns 0. + + Example: + + UInt32 ip = 0; + for () + { + ; size must be >= Alignment + LookAhead, if it's not last block + SizeT processed = Convert(data, size, ip, 1); + data += processed; + size -= processed; + ip += processed; + } +*/ + +#define x86_Convert_Init(state) { state = 0; } +SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding); +SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); + +#endif diff --git a/lzma/C/Bra86.c b/lzma/C/Bra86.c new file mode 100644 index 000000000..93566cb21 --- /dev/null +++ b/lzma/C/Bra86.c @@ -0,0 +1,85 @@ +/* Bra86.c -- Converter for x86 code (BCJ) +2008-10-04 : Igor Pavlov : Public domain */ + +#include "Bra.h" + +#define Test86MSByte(b) ((b) == 0 || (b) == 0xFF) + +const Byte kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0}; +const Byte kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3}; + +SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding) +{ + SizeT bufferPos = 0, prevPosT; + UInt32 prevMask = *state & 0x7; + if (size < 5) + return 0; + ip += 5; + prevPosT = (SizeT)0 - 1; + + for (;;) + { + Byte *p = data + bufferPos; + Byte *limit = data + size - 4; + for (; p < limit; p++) + if ((*p & 0xFE) == 0xE8) + break; + bufferPos = (SizeT)(p - data); + if (p >= limit) + break; + prevPosT = bufferPos - prevPosT; + if (prevPosT > 3) + prevMask = 0; + else + { + prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7; + if (prevMask != 0) + { + Byte b = p[4 - kMaskToBitNumber[prevMask]]; + if (!kMaskToAllowedStatus[prevMask] || Test86MSByte(b)) + { + prevPosT = bufferPos; + prevMask = ((prevMask << 1) & 0x7) | 1; + bufferPos++; + continue; + } + } + } + prevPosT = bufferPos; + + if (Test86MSByte(p[4])) + { + UInt32 src = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]); + UInt32 dest; + for (;;) + { + Byte b; + int index; + if (encoding) + dest = (ip + (UInt32)bufferPos) + src; + else + dest = src - (ip + (UInt32)bufferPos); + if (prevMask == 0) + break; + index = kMaskToBitNumber[prevMask] * 8; + b = (Byte)(dest >> (24 - index)); + if (!Test86MSByte(b)) + break; + src = dest ^ ((1 << (32 - index)) - 1); + } + p[4] = (Byte)(~(((dest >> 24) & 1) - 1)); + p[3] = (Byte)(dest >> 16); + p[2] = (Byte)(dest >> 8); + p[1] = (Byte)dest; + bufferPos += 5; + } + else + { + prevMask = ((prevMask << 1) & 0x7) | 1; + bufferPos++; + } + } + prevPosT = bufferPos - prevPosT; + *state = ((prevPosT > 3) ? 0 : ((prevMask << ((int)prevPosT - 1)) & 0x7)); + return bufferPos; +} diff --git a/lzma/C/CpuArch.h b/lzma/C/CpuArch.h new file mode 100644 index 000000000..006361f2f --- /dev/null +++ b/lzma/C/CpuArch.h @@ -0,0 +1,69 @@ +/* CpuArch.h +2008-08-05 +Igor Pavlov +Public domain */ + +#ifndef __CPUARCH_H +#define __CPUARCH_H + +/* +LITTLE_ENDIAN_UNALIGN means: + 1) CPU is LITTLE_ENDIAN + 2) it's allowed to make unaligned memory accesses +if LITTLE_ENDIAN_UNALIGN is not defined, it means that we don't know +about these properties of platform. +*/ + +#if defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || defined(__i386__) || defined(__x86_64__) +#define LITTLE_ENDIAN_UNALIGN +#endif + +#ifdef LITTLE_ENDIAN_UNALIGN + +#define GetUi16(p) (*(const UInt16 *)(p)) +#define GetUi32(p) (*(const UInt32 *)(p)) +#define GetUi64(p) (*(const UInt64 *)(p)) +#define SetUi32(p, d) *(UInt32 *)(p) = (d); + +#else + +#define GetUi16(p) (((const Byte *)(p))[0] | ((UInt16)((const Byte *)(p))[1] << 8)) + +#define GetUi32(p) ( \ + ((const Byte *)(p))[0] | \ + ((UInt32)((const Byte *)(p))[1] << 8) | \ + ((UInt32)((const Byte *)(p))[2] << 16) | \ + ((UInt32)((const Byte *)(p))[3] << 24)) + +#define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32)) + +#define SetUi32(p, d) { UInt32 _x_ = (d); \ + ((Byte *)(p))[0] = (Byte)_x_; \ + ((Byte *)(p))[1] = (Byte)(_x_ >> 8); \ + ((Byte *)(p))[2] = (Byte)(_x_ >> 16); \ + ((Byte *)(p))[3] = (Byte)(_x_ >> 24); } + +#endif + +#if defined(LITTLE_ENDIAN_UNALIGN) && defined(_WIN64) && (_MSC_VER >= 1300) + +#pragma intrinsic(_byteswap_ulong) +#pragma intrinsic(_byteswap_uint64) +#define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p)) +#define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p)) + +#else + +#define GetBe32(p) ( \ + ((UInt32)((const Byte *)(p))[0] << 24) | \ + ((UInt32)((const Byte *)(p))[1] << 16) | \ + ((UInt32)((const Byte *)(p))[2] << 8) | \ + ((const Byte *)(p))[3] ) + +#define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4)) + +#endif + +#define GetBe16(p) (((UInt16)((const Byte *)(p))[0] << 8) | ((const Byte *)(p))[1]) + +#endif diff --git a/lzma/C/Types.h b/lzma/C/Types.h index ebac878bb..818d92d5d 100644 --- a/lzma/C/Types.h +++ b/lzma/C/Types.h @@ -6,6 +6,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + #define SZ_OK 0 #define SZ_ERROR_DATA 1 @@ -197,4 +201,8 @@ typedef struct #define IAlloc_Alloc(p, size) (p)->Alloc((p), size) #define IAlloc_Free(p, a) (p)->Free((p), a) +#ifdef __cplusplus +} +#endif + #endif diff --git a/lzma/CMakeLists.txt b/lzma/CMakeLists.txt index c522f6c35..b9b4bc047 100644 --- a/lzma/CMakeLists.txt +++ b/lzma/CMakeLists.txt @@ -5,7 +5,17 @@ if( CMAKE_COMPILER_IS_GNUC ) endif( CMAKE_COMPILER_IS_GNUC ) set( LZMA_FILES + C/Archive/7z/7zDecode.c + C/Archive/7z/7zExtract.c + C/Archive/7z/7zHeader.c + C/Archive/7z/7zIn.c + C/Archive/7z/7zItem.c + C/7zBuf.c + C/7zCrc.c + C/7zStream.c C/Alloc.c + C/Bcj2.c + C/Bra86.c C/LzFind.c C/LzFindMt.c C/LzmaDec.c diff --git a/lzma/lzmalib.vcproj b/lzma/lzmalib.vcproj index f59e6d472..414e26f8d 100644 --- a/lzma/lzmalib.vcproj +++ b/lzma/lzmalib.vcproj @@ -286,10 +286,30 @@ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" > + + + + + + + + + + @@ -320,6 +340,14 @@ Filter="h;hpp;hxx;hm;inl;inc;xsd" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > + + + + @@ -328,6 +356,18 @@ RelativePath=".\C\Alloc.h" > + + + + + + @@ -361,6 +401,50 @@ > + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/files.cpp b/src/files.cpp index 6d5beacbb..5705b938a 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -364,7 +364,7 @@ extern "C" void bz_internal_error (int errcode) static void *SzAlloc(void *p, size_t size) { p = p; return malloc(size); } static void SzFree(void *p, void *address) { p = p; free(address); } -static ISzAlloc g_Alloc = { SzAlloc, SzFree }; +ISzAlloc g_Alloc = { SzAlloc, SzFree }; FileReaderLZMA::FileReaderLZMA (FileReader &file, size_t uncompressed_size, bool zip) : File(file), SawEOF(false) diff --git a/src/w_wad.cpp b/src/w_wad.cpp index 02f433dcb..12d2b651a 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -30,10 +30,21 @@ #include "templates.h" #include "gi.h" +extern "C" { +#include "Archive/7z/7zHeader.h" +#include "Archive/7z/7zExtract.h" +#include "Archive/7z/7zIn.h" +#include "7zCrc.h" +} + // MACROS ------------------------------------------------------------------ #define NULL_INDEX (0xffff) +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern ISzAlloc g_Alloc; + // TYPES ------------------------------------------------------------------- struct rffinfo_t @@ -81,6 +92,7 @@ union MergedHeader wadinfo_t wad; rffinfo_t rff; grpinfo_t grp; + BYTE sevenzip[k7zSignatureSize]; }; @@ -92,32 +104,149 @@ struct FWadCollection::LumpRecord char * fullname; // only valid for files loaded from a .zip file char name[9]; BYTE method; // zip compression method + BYTE flags; short wadnum; - WORD flags; int position; int size; int namespc; int compressedsize; + + void ZipNameSetup(char *name); +}; + +struct CZDFileInStream +{ + ISeekInStream s; + FileReader *File; + + CZDFileInStream(FileReader *_file) + { + s.Read = Read; + s.Seek = Seek; + File = _file; + } + + static SRes Read(void *pp, void *buf, size_t *size) + { + CZDFileInStream *p = (CZDFileInStream *)pp; + long numread = p->File->Read(buf, (long)*size); + if (numread < 0) + { + *size = 0; + return SZ_ERROR_READ; + } + *size = numread; + return SZ_OK; + } + + static SRes Seek(void *pp, Int64 *pos, ESzSeek origin) + { + CZDFileInStream *p = (CZDFileInStream *)pp; + int move_method; + int res; + if (origin == SZ_SEEK_SET) + { + move_method = SEEK_SET; + } + else if (origin == SZ_SEEK_CUR) + { + move_method = SEEK_CUR; + } + else if (origin == SZ_SEEK_END) + { + move_method = SEEK_END; + } + else + { + return 1; + } + res = p->File->Seek((long)*pos, move_method); + *pos = p->File->Tell(); + return res; + } +}; + +struct C7zArchive +{ + CSzArEx DB; + CZDFileInStream ArchiveStream; + CLookToRead LookStream; + UInt32 BlockIndex; + Byte *OutBuffer; + size_t OutBufferSize; + + C7zArchive(FileReader *file) : ArchiveStream(file) + { + if (g_CrcTable[1] == 0) + { + CrcGenerateTable(); + } + file->Seek(0, SEEK_SET); + LookToRead_CreateVTable(&LookStream, false); + LookStream.realStream = &ArchiveStream.s; + LookToRead_Init(&LookStream); + SzArEx_Init(&DB); + BlockIndex = 0xFFFFFFFF; + OutBuffer = NULL; + OutBufferSize = 0; + } + + ~C7zArchive() + { + if (OutBuffer != NULL) + { + IAlloc_Free(&g_Alloc, OutBuffer); + } + SzArEx_Free(&DB, &g_Alloc); + } + + SRes Open() + { + return SzArEx_Open(&DB, &LookStream.s, &g_Alloc, &g_Alloc); + } + + SRes Extract(UInt32 file_index, char *buffer) + { + size_t offset, out_size_processed; + SRes res = SzAr_Extract(&DB, &LookStream.s, file_index, + &BlockIndex, &OutBuffer, &OutBufferSize, + &offset, &out_size_processed, + &g_Alloc, &g_Alloc); + if (res == SZ_OK) + { + memcpy(buffer, OutBuffer + offset, out_size_processed); + } + return res; + } }; class FWadCollection::WadFileRecord : public FileReader { public: WadFileRecord (FILE *file); - WadFileRecord (const char * buffer, int length); + WadFileRecord (const char *buffer, int length); ~WadFileRecord (); long Seek (long offset, int origin); long Read (void *buffer, long len); - const char * MemoryData; + const char *MemoryData; + C7zArchive *Archive; - char *Name; + FString Name; DWORD FirstLump; DWORD LastLump; - }; +struct FEmbeddedWAD +{ + union + { + C7zArchive *Archive; + FZipCentralDirectoryInfo *Zip; + }; + int Position; +}; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -129,8 +258,6 @@ void W_SysWadInit (); static void PrintLastError (); -// EXTERNAL DATA DECLARATIONS ---------------------------------------------- - // PUBLIC DATA DEFINITIONS ------------------------------------------------- FWadCollection Wads; @@ -285,7 +412,7 @@ int FWadCollection::AddExternalFile(const char *filename) lump.fullname = copystring(filename); memset(lump.name, 0, sizeof(lump.name)); - lump.wadnum=-1; + lump.wadnum = -1; lump.flags = LUMPF_EXTERNAL; lump.position = 0; lump.namespc = ns_global; @@ -365,7 +492,7 @@ int STACK_ARGS FWadCollection::lumpcmp(const void * a, const void * b) } -void FWadCollection::AddFile (const char *filename, const char * data, int length) +void FWadCollection::AddFile (const char *filename, const char *data, int length) { WadFileRecord *wadinfo; MergedHeader header; @@ -375,10 +502,10 @@ void FWadCollection::AddFile (const char *filename, const char * data, int lengt int startlump; wadlump_t* fileinfo = NULL, *fileinfo2free = NULL; wadlump_t singleinfo; - TArray EmbeddedWADs; - void * directory = NULL; + TArray EmbeddedWADs; + void *directory = NULL; - if (length==-1) + if (length == -1) { // open the file and add to directory handle = fopen (filename, "rb"); @@ -409,7 +536,7 @@ void FWadCollection::AddFile (const char *filename, const char * data, int lengt return; } - wadinfo->Name = copystring (filename); + wadinfo->Name = filename; if (header.magic[0] == IWAD_ID || header.magic[0] == PWAD_ID) { // This is a WAD file @@ -516,6 +643,7 @@ void FWadCollection::AddFile (const char *filename, const char * data, int lengt if (centraldir == 0) { Printf("\n%s: ZIP file corrupt!\n", filename); + delete wadinfo; return; } @@ -528,6 +656,7 @@ void FWadCollection::AddFile (const char *filename, const char * data, int lengt info.FirstDisk != 0 || info.DiskNumber != 0) { Printf("\n%s: Multipart Zip files are not supported.\n", filename); + delete wadinfo; return; } @@ -546,7 +675,6 @@ void FWadCollection::AddFile (const char *filename, const char * data, int lengt FZipCentralDirectoryInfo *zip_fh = (FZipCentralDirectoryInfo *)dirptr; char name[256]; - char base[256]; int len = LittleShort(zip_fh->NameLength); strncpy(name, dirptr + sizeof(FZipCentralDirectoryInfo), MIN(len, 255)); @@ -589,68 +717,24 @@ void FWadCollection::AddFile (const char *filename, const char * data, int lengt // They must be extracted and added separately to the lump list. // WADs in subdirectories are added to the lump directory. // Embedded .zips are ignored for now. But they should be allowed later! - char * c = strstr(name, ".wad"); - if (c && strlen(c)==4 && !strchr(name, '/')) + char *c = strstr(name, ".wad"); + if (c && strlen(c) == 4 && !strchr(name, '/')) { - EmbeddedWADs.Push(zip_fh); + FEmbeddedWAD embed; + embed.Zip = zip_fh; + embed.Position = -1; + EmbeddedWADs.Push(embed); skipped++; continue; } - //ExtractFileBase(name, base); - char *lname = strrchr(name,'/'); - if (!lname) lname = name; - else lname++; - strcpy(base, lname); - char *dot = strrchr(base, '.'); - if (dot) *dot = 0; - uppercopy(lump_p->name, base); - lump_p->name[8] = 0; - lump_p->fullname = copystring(name); + lump_p->ZipNameSetup(name); lump_p->size = LittleLong(zip_fh->UncompressedSize); - - // Map some directories to WAD namespaces. - // Note that some of these namespaces don't exist in WADS. - // CheckNumForName will handle any request for these namespaces accordingly. - lump_p->namespc = !strncmp(name, "flats/", 6) ? ns_flats : - !strncmp(name, "textures/", 9) ? ns_newtextures : - !strncmp(name, "hires/", 6) ? ns_hires : - !strncmp(name, "sprites/", 8) ? ns_sprites : - !strncmp(name, "colormaps/", 10) ? ns_colormaps : - !strncmp(name, "acs/", 4) ? ns_acslibrary : - !strncmp(name, "voices/", 7) ? ns_strifevoices : - !strncmp(name, "patches/", 8) ? ns_patches : - !strncmp(name, "graphics/", 9) ? ns_graphics : - !strncmp(name, "sounds/", 7) ? ns_sounds : - !strncmp(name, "music/", 6) ? ns_music : - !strchr(name, '/') ? ns_global : -1; - - // Anything that is not in one of these subdirectories or the main directory - // should not be accessible through the standard WAD functions but only through - // the ones which look for the full name. - if (lump_p->namespc == -1) - { - memset(lump_p->name, 0, 8); - } - lump_p->wadnum = (WORD)Wads.Size(); lump_p->flags = (zip_fh->Method != METHOD_STORED) ? LUMPF_COMPRESSED|LUMPF_ZIPFILE : LUMPF_ZIPFILE; lump_p->method = zip_fh->Method; lump_p->compressedsize = LittleLong(zip_fh->CompressedSize); - // Since '\' can't be used as a file name's part inside a ZIP - // we have to work around this for sprites because it is a valid - // frame character. - if (lump_p->namespc == ns_sprites) - { - char * c; - - while ((c = (char*)memchr(lump_p->name, '^', 8))) - { - *c = '\\'; - } - } - // The start of the file will be determined the first time it is accessed. lump_p->flags |= LUMPF_NEEDFILESTART; lump_p->position = LittleLong(zip_fh->LocalHeaderOffset); @@ -659,10 +743,91 @@ void FWadCollection::AddFile (const char *filename, const char * data, int lengt // Resize the lump record array to its actual size NumLumps -= skipped; LumpInfo.Resize(NumLumps); + Printf (" (%d files)", LittleShort(info.NumEntries) - skipped); // Entries in Zips are sorted alphabetically. qsort(&LumpInfo[startlump], NumLumps - startlump, sizeof(LumpRecord), lumpcmp); } + else if (memcmp(header.sevenzip, k7zSignature, k7zSignatureSize) == 0) + { + C7zArchive *arc = new C7zArchive(wadinfo); + int skipped = 0; + SRes res; + + res = arc->Open(); + if (res != SZ_OK) + { + delete arc; + delete wadinfo; + Printf("\n%s: ", filename); + if (res == SZ_ERROR_UNSUPPORTED) + { + Printf("Decoder does not support this archive\n"); + } + else if (res == SZ_ERROR_MEM) + { + Printf("Cannot allocate memory\n"); + } + else if (res == SZ_ERROR_CRC) + { + Printf("CRC error\n"); + } + else + { + Printf("error #%d\n", res); + } + return; + } + wadinfo->Archive = arc; + NumLumps += arc->DB.db.NumFiles; + LumpInfo.Resize(NumLumps); + lump_p = &LumpInfo[startlump]; + + for (int i = 0; i < arc->DB.db.NumFiles; ++i) + { + CSzFileItem *file = &arc->DB.db.Files[i]; + char name[256]; + + // skip Directories + if (file->IsDir) + { + skipped++; + continue; + } + + strncpy(name, file->Name, countof(name)-1); + name[countof(name)-1] = 0; + FixPathSeperator(name); + strlwr(name); + + // Check for embedded WADs in the root directory. + char *c = strstr(name, ".wad"); + if (c && strlen(c) == 4 && !strchr(name, '/')) + { + FEmbeddedWAD embed; + embed.Archive = arc; + embed.Position = i; + EmbeddedWADs.Push(embed); + skipped++; + continue; + } + + lump_p->ZipNameSetup(name); + lump_p->size = file->Size; + lump_p->wadnum = (WORD)Wads.Size(); + lump_p->flags = LUMPF_7ZFILE; + lump_p->position = i; + lump_p->compressedsize = -1; + lump_p++; + } + // Resize the lump record array to its actual size + NumLumps -= skipped; + LumpInfo.Resize(NumLumps); + Printf (" (%u files)", arc->DB.db.NumFiles - skipped); + + // Entries in archives are sorted alphabetically + qsort(&LumpInfo[startlump], NumLumps - startlump, sizeof(LumpRecord), lumpcmp); + } else { // This is just a single lump file fileinfo2free = NULL; @@ -678,7 +843,8 @@ void FWadCollection::AddFile (const char *filename, const char * data, int lengt // Fill in lumpinfo if (header.magic[0] != RFF_ID && header.magic[0] != ZIP_ID && - (header.magic[0] != GRP_ID_0 || header.magic[1] != GRP_ID_1 || header.magic[2] != GRP_ID_2)) + (header.magic[0] != GRP_ID_0 || header.magic[1] != GRP_ID_1 || header.magic[2] != GRP_ID_2) && + memcmp(header.sevenzip, k7zSignature, k7zSignatureSize)) { LumpInfo.Resize(NumLumps); lump_p = &LumpInfo[startlump]; @@ -717,54 +883,147 @@ void FWadCollection::AddFile (const char *filename, const char * data, int lengt if (EmbeddedWADs.Size()) { char path[256]; + size_t len; + char *buffer; mysnprintf(path, countof(path), "%s:", filename); - char * wadstr = path+strlen(path); + char *wadstr = path + strlen(path); for(unsigned int i = 0; i < EmbeddedWADs.Size(); i++) { - FZipCentralDirectoryInfo *zip_fh = EmbeddedWADs[i]; - FZipLocalFileHeader localHeader; + FEmbeddedWAD *embed = &EmbeddedWADs[i]; - *wadstr = 0; - size_t len = LittleShort(zip_fh->NameLength); - if (len + strlen(path) > 255) len = 255 - strlen(path); - strncpy(wadstr, ((char*)zip_fh) + sizeof(FZipCentralDirectoryInfo), len); - wadstr[len] = 0; - - DWORD size = LittleLong(zip_fh->UncompressedSize); - char *buffer = new char[size]; - - int position = LittleLong(zip_fh->LocalHeaderOffset) ; - - wadinfo->Seek(position, SEEK_SET); - wadinfo->Read(&localHeader, sizeof(localHeader)); - position += sizeof(FZipLocalFileHeader) + LittleShort(localHeader.ExtraLength) + LittleShort(zip_fh->NameLength); - - wadinfo->Seek(position, SEEK_SET); - if (LittleShort(zip_fh->Method) == METHOD_DEFLATE) + if (embed->Position == -1) { - FileReaderZ frz(*wadinfo, true); - frz.Read(buffer, size); - } - else if (LittleShort(zip_fh->Method) == METHOD_LZMA) - { - FileReaderLZMA frz(*wadinfo, size, true); - frz.Read(buffer, size); - } - else if (LittleShort(zip_fh->Method) == METHOD_BZIP2) - { - FileReaderBZ2 frz(*wadinfo); - frz.Read(buffer, size); + FZipCentralDirectoryInfo *zip_fh = embed->Zip; + FZipLocalFileHeader localHeader; + + *wadstr = 0; + len = LittleShort(zip_fh->NameLength); + if (len + strlen(path) > 255) + { + len = 255 - strlen(path); + } + strncpy(wadstr, ((char*)zip_fh) + sizeof(FZipCentralDirectoryInfo), len); + wadstr[len] = 0; + + DWORD size = LittleLong(zip_fh->UncompressedSize); + buffer = new char[size]; + + int position = LittleLong(zip_fh->LocalHeaderOffset); + + wadinfo->Seek(position, SEEK_SET); + wadinfo->Read(&localHeader, sizeof(localHeader)); + position += sizeof(FZipLocalFileHeader) + LittleShort(localHeader.ExtraLength) + LittleShort(zip_fh->NameLength); + + wadinfo->Seek(position, SEEK_SET); + if (LittleShort(zip_fh->Method) == METHOD_DEFLATE) + { + FileReaderZ frz(*wadinfo, true); + frz.Read(buffer, size); + } + else if (LittleShort(zip_fh->Method) == METHOD_LZMA) + { + FileReaderLZMA frz(*wadinfo, size, true); + frz.Read(buffer, size); + } + else if (LittleShort(zip_fh->Method) == METHOD_BZIP2) + { + FileReaderBZ2 frz(*wadinfo); + frz.Read(buffer, size); + } + else + { + wadinfo->Read(buffer, size); + } + AddFile(path, buffer, size); } else { - wadinfo->Read(buffer, size); + CSzFileItem *file = &embed->Archive->DB.db.Files[embed->Position]; + len = strlen(file->Name); + if (len + strlen(path) > 255) + { + len = 255 - strlen(path); + } + strncpy(wadstr, file->Name, len); + wadstr[len] = 0; + + buffer = new char[file->Size]; + if (embed->Archive->Extract(embed->Position, buffer) == SZ_OK) + { + AddFile(path, buffer, file->Size); + } } - AddFile(path, buffer, size); } } - if (directory != NULL) free(directory); + if (directory != NULL) + { + free(directory); + } +} + +//========================================================================== +// +// FWadCollection :: LumpRecord :: ZipNameSetup +// +// For lumps from an archive, determine this lump's wad-compatible name, +// namespace, and set the fullname. +// +//========================================================================== + +void FWadCollection::LumpRecord::ZipNameSetup(char *iname) +{ + char base[256]; + char *lname = strrchr(iname,'/'); + lname = (lname == NULL) ? iname : lname + 1; + strcpy(base, lname); + char *dot = strrchr(base, '.'); + if (dot != NULL) + { + *dot = 0; + } + uppercopy(name, base); + name[8] = 0; + fullname = copystring(iname); + + // Map some directories to WAD namespaces. + // Note that some of these namespaces don't exist in WADS. + // CheckNumForName will handle any request for these namespaces accordingly. + namespc = !strncmp(iname, "flats/", 6) ? ns_flats : + !strncmp(iname, "textures/", 9) ? ns_newtextures : + !strncmp(iname, "hires/", 6) ? ns_hires : + !strncmp(iname, "sprites/", 8) ? ns_sprites : + !strncmp(iname, "colormaps/", 10) ? ns_colormaps : + !strncmp(iname, "acs/", 4) ? ns_acslibrary : + !strncmp(iname, "voices/", 7) ? ns_strifevoices : + !strncmp(iname, "patches/", 8) ? ns_patches : + !strncmp(iname, "graphics/", 9) ? ns_graphics : + !strncmp(iname, "sounds/", 7) ? ns_sounds : + !strncmp(iname, "music/", 6) ? ns_music : + !strchr(iname, '/') ? ns_global : + -1; + + // Anything that is not in one of these subdirectories or the main directory + // should not be accessible through the standard WAD functions but only through + // the ones which look for the full name. + if (namespc == -1) + { + memset(name, 0, 8); + } + + // Since '\' can't be used as a file name's part inside a ZIP + // we have to work around this for sprites because it is a valid + // frame character. + else if (namespc == ns_sprites) + { + char *c; + + while ((c = (char*)memchr(name, '^', 8))) + { + *c = '\\'; + } + } } //========================================================================== @@ -1797,39 +2056,29 @@ FWadLump FWadCollection::OpenLumpNum (int lump) } l = &LumpInfo[lump]; - wad = l->wadnum >= 0? Wads[l->wadnum] : NULL; + wad = l->wadnum >= 0 ? Wads[l->wadnum] : NULL; if (l->flags & LUMPF_NEEDFILESTART) { SetLumpAddress(l); } - if (wad != NULL) wad->Seek (l->position, SEEK_SET); + if (l->flags & LUMPF_7ZFILE) + { + // An entry in a .7z file + char *buffer = new char[l->size + 1]; // the last byte is used as a reference counter - if (l->flags & LUMPF_COMPRESSED) + } + + if (wad != NULL) + { + wad->Seek (l->position, SEEK_SET); + } + + if (l->flags & (LUMPF_COMPRESSED | LUMPF_7ZFILE)) { // A compressed entry in a .zip file - char *buffer = new char[l->size + 1]; // the last byte is used as a reference counter - buffer[l->size] = 0; - if (l->method == METHOD_DEFLATE) - { - FileReaderZ frz(*wad, true); - frz.Read(buffer, l->size); - } - else if (l->method == METHOD_LZMA) - { - FileReaderLZMA frz(*wad, l->size, true); - frz.Read(buffer, l->size); - } - else if (l->method == METHOD_BZIP2) - { - FileReaderBZ2 frz(*wad); - frz.Read(buffer, l->size); - } - else - { - assert(0); // Should not get here - } + char *buffer = ReadZipLump(l); return FWadLump(buffer, l->size, true); } else if (l->flags & LUMPF_EXTERNAL) @@ -1841,7 +2090,7 @@ FWadLump FWadCollection::OpenLumpNum (int lump) { FString name; - name.Format("%s/%s", wad->Name, l->fullname); + name << wad->Name << '/' << l->fullname; f = fopen(name, "rb"); } else @@ -1898,38 +2147,18 @@ FWadLump *FWadCollection::ReopenLumpNum (int lump) } l = &LumpInfo[lump]; - wad = l->wadnum >= 0? Wads[l->wadnum] : NULL; + wad = l->wadnum >= 0 ? Wads[l->wadnum] : NULL; if (l->flags & LUMPF_NEEDFILESTART) { SetLumpAddress(l); } - if (l->flags & LUMPF_COMPRESSED) + if (l->flags & (LUMPF_COMPRESSED | LUMPF_7ZFILE)) { // A compressed entry in a .zip file int address = wad->Tell(); // read from the existing WadFileRecord without reopening - char *buffer = new char[l->size + 1]; // the last byte is used as a reference counter - wad->Seek(l->position, SEEK_SET); - if (l->method == METHOD_DEFLATE) - { - FileReaderZ frz(*wad, true); - frz.Read(buffer, l->size); - } - else if (l->method == METHOD_LZMA) - { - FileReaderLZMA frz(*wad, l->size, true); - frz.Read(buffer, l->size); - } - else if (l->method == METHOD_BZIP2) - { - FileReaderBZ2 frz(*wad); - frz.Read(buffer, l->size); - } - else - { - assert(0); // Should not get here - } + char *buffer = ReadZipLump(l); wad->Seek(address, SEEK_SET); return new FWadLump(buffer, l->size, true); //... but restore the file pointer afterward! } @@ -1942,7 +2171,7 @@ FWadLump *FWadCollection::ReopenLumpNum (int lump) { FString name; - name.Format("%s/%s", wad->Name, l->fullname); + name << wad->Name << '/' << l->fullname; f = fopen(name, "rb"); } else @@ -1977,7 +2206,7 @@ FWadLump *FWadCollection::ReopenLumpNum (int lump) f = fopen (wad->Name, "rb"); if (f == NULL) { - I_Error ("Could not reopen %s\n", wad->Name); + I_Error ("Could not reopen %s\n", wad->Name.GetChars()); } fseek (f, l->position, SEEK_SET); @@ -1986,6 +2215,50 @@ FWadLump *FWadCollection::ReopenLumpNum (int lump) } +//========================================================================== +// +// FWadCollection :: ReadZipLump +// +// Extracts a compressed file from a zip into memory. +// +//========================================================================== + +char *FWadCollection::ReadZipLump(LumpRecord *l) +{ + WadFileRecord *wad = Wads[l->wadnum]; + char *buffer = new char[l->size + 1]; // the last byte is used as a reference counter + buffer[l->size] = 0; + + if (!(l->flags & LUMPF_7ZFILE)) + { + wad->Seek(l->position, SEEK_SET); + if (l->method == METHOD_DEFLATE) + { + FileReaderZ frz(*wad, true); + frz.Read(buffer, l->size); + } + else if (l->method == METHOD_LZMA) + { + FileReaderLZMA frz(*wad, l->size, true); + frz.Read(buffer, l->size); + } + else if (l->method == METHOD_BZIP2) + { + FileReaderBZ2 frz(*wad); + frz.Read(buffer, l->size); + } + else + { + assert(0); // Should not get here + } + } + else + { + wad->Archive->Extract(l->position, buffer); + } + return buffer; +} + //========================================================================== // // GetFileReader @@ -2142,7 +2415,7 @@ void FWadCollection::SkinHack (int baselump) "The maps in %s will not be loaded because it has a skin.\n" TEXTCOLOR_BLUE "You should remove the skin from the wad to play these maps.\n", - Wads[LumpInfo[baselump].wadnum]->Name); + Wads[LumpInfo[baselump].wadnum]->Name.GetChars()); } } @@ -2165,13 +2438,12 @@ void BloodCrypt (void *data, int key, int len) // WadFileRecord ------------------------------------------------------------ FWadCollection::WadFileRecord::WadFileRecord (FILE *file) -: FileReader(file), Name(NULL), FirstLump(0), LastLump(0) +: FileReader(file), MemoryData(NULL), Archive(NULL), FirstLump(0), LastLump(0) { - MemoryData=NULL; } -FWadCollection::WadFileRecord::WadFileRecord (const char * mem, int len) -: FileReader(), MemoryData(mem), Name(NULL), FirstLump(0), LastLump(0) +FWadCollection::WadFileRecord::WadFileRecord (const char *mem, int len) +: FileReader(), MemoryData(mem), Archive(NULL), FirstLump(0), LastLump(0) { Length = len; FilePos = StartPos = 0; @@ -2179,55 +2451,62 @@ FWadCollection::WadFileRecord::WadFileRecord (const char * mem, int len) FWadCollection::WadFileRecord::~WadFileRecord () { - if (Name != NULL) - { - delete[] Name; - } if (MemoryData != NULL) { - delete [] MemoryData; + delete[] MemoryData; + } + if (Archive != NULL) + { + delete Archive; } } long FWadCollection::WadFileRecord::Seek (long offset, int origin) { - if (MemoryData==NULL) return FileReader::Seek(offset, origin); + if (MemoryData == NULL) + { + return FileReader::Seek(offset, origin); + } else { switch (origin) { case SEEK_CUR: - offset+=FilePos; + offset += FilePos; break; case SEEK_END: - offset+=Length; + offset += Length; break; + default: break; } - if (offset<0) offset=0; - else if (offset>Length) offset=Length; - FilePos=offset; + FilePos = clamp(offset, 0, Length); return 0; } } + long FWadCollection::WadFileRecord::Read (void *buffer, long len) { - if (MemoryData==NULL) return FileReader::Read(buffer, len); + if (MemoryData == NULL) + { + return FileReader::Read(buffer, len); + } else { - if (FilePos+len>Length) len=Length-FilePos; - memcpy(buffer, MemoryData+FilePos, len); - FilePos+=len; + if (FilePos + len > Length) + { + len = Length - FilePos; + } + memcpy(buffer, MemoryData + FilePos, len); + FilePos += len; return len; } } // FWadLump ----------------------------------------------------------------- -// FWadLump ----------------------------------------------------------------- - FWadLump::FWadLump () : FileReader(), SourceData(NULL), DestroySource(false), Encrypted(false) { diff --git a/src/w_wad.h b/src/w_wad.h index 31ee6315a..ec6cbfec4 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -69,6 +69,7 @@ enum LUMPF_ZIPFILE = 4, // Inside a Zip file - used to enforce use of special directories insize Zips LUMPF_NEEDFILESTART = 8, // Still need to process local file header to find file start inside a zip LUMPF_EXTERNAL = 16, // Lump is from an external file that won't be kept open permanently + LUMPF_7ZFILE = 32, // Inside a 7z archive; position is its index in the archive }; @@ -168,7 +169,7 @@ public: enum { IWAD_FILENUM = 1 }; void InitMultipleFiles (wadlist_t **filenames); - void AddFile (const char *filename, const char * data=NULL,int length=-1); + void AddFile (const char *filename, const char *data=NULL, int length=-1); int CheckIfWadLoaded (const char *name); const char *GetWadName (int wadnum) const; @@ -235,7 +236,7 @@ protected: TArray LumpInfo; - TArrayWads; + TArray Wads; DWORD NumLumps; // Not necessarily the same as LumpInfo.Size() DWORD NumWads; @@ -247,6 +248,8 @@ protected: bool IsMarker (const LumpRecord *lump, const char *marker) const; void FindStrifeTeaserVoices (); + char *ReadZipLump(LumpRecord *l); + private: static int STACK_ARGS lumpcmp(const void * a, const void * b); void ScanForFlatHack (int startlump);