/* ** file_7z.cpp ** **--------------------------------------------------------------------------- ** Copyright 2009 Randy Heit ** Copyright 2009 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** ** */ // Note that 7z made the unwise decision to include windows.h :( #include "7z.h" #include "7zCrc.h" #include "resourcefile.h" #include "cmdlib.h" #include "printf.h" //#include "v_text.h" //#include "w_wad.h" //----------------------------------------------------------------------- // // Interface classes to 7z library // //----------------------------------------------------------------------- extern ISzAlloc g_Alloc; struct CZDFileInStream { ISeekInStream s; FileReader &File; CZDFileInStream(FileReader &_file) : File(_file) { s.Read = Read; s.Seek = Seek; } static SRes Read(const ISeekInStream *pp, void *buf, size_t *size) { CZDFileInStream *p = (CZDFileInStream *)pp; auto numread = p->File.Read(buf, (long)*size); if (numread < 0) { *size = 0; return SZ_ERROR_READ; } *size = numread; return SZ_OK; } static SRes Seek(const ISeekInStream *pp, Int64 *pos, ESzSeek origin) { CZDFileInStream *p = (CZDFileInStream *)pp; FileReader::ESeek move_method; int res; if (origin == SZ_SEEK_SET) { move_method = FileReader::SeekSet; } else if (origin == SZ_SEEK_CUR) { move_method = FileReader::SeekCur; } else if (origin == SZ_SEEK_END) { move_method = FileReader::SeekEnd; } else { return 1; } res = (int)p->File.Seek((long)*pos, move_method); *pos = p->File.Tell(); return res; } }; struct C7zArchive { CSzArEx DB; CZDFileInStream ArchiveStream; CLookToRead2 LookStream; Byte StreamBuffer[1<<14]; UInt32 BlockIndex; Byte *OutBuffer; size_t OutBufferSize; C7zArchive(FileReader &file) : ArchiveStream(file) { if (g_CrcTable[1] == 0) { CrcGenerateTable(); } file.Seek(0, FileReader::SeekSet); LookToRead2_CreateVTable(&LookStream, false); LookStream.realStream = &ArchiveStream.s; LookToRead2_Init(&LookStream); LookStream.bufSize = sizeof(StreamBuffer); LookStream.buf = StreamBuffer; 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.vt, &g_Alloc, &g_Alloc); } SRes Extract(UInt32 file_index, char *buffer) { size_t offset, out_size_processed; SRes res = SzArEx_Extract(&DB, &LookStream.vt, 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; } }; //========================================================================== // // Zip Lump // //========================================================================== struct F7ZLump : public FResourceLump { int Position; virtual int FillCache(); }; //========================================================================== // // 7-zip file // //========================================================================== class F7ZFile : public FResourceFile { friend struct F7ZLump; F7ZLump *Lumps; C7zArchive *Archive; public: F7ZFile(const char * filename, FileReader &filer); bool Open(bool quiet); virtual ~F7ZFile(); virtual FResourceLump *GetLump(int no) { return ((unsigned)no < NumLumps)? &Lumps[no] : NULL; } }; //========================================================================== // // 7Z file // //========================================================================== F7ZFile::F7ZFile(const char * filename, FileReader &filer) : FResourceFile(filename, filer) { Lumps = NULL; Archive = NULL; } //========================================================================== // // Open it // //========================================================================== bool F7ZFile::Open(bool quiet) { Archive = new C7zArchive(Reader); int skipped = 0; SRes res; res = Archive->Open(); if (res != SZ_OK) { delete Archive; Archive = NULL; if (!quiet) { Printf("\n%s: ", FileName.GetChars()); 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 false; } CSzArEx* const archPtr = &Archive->DB; NumLumps = archPtr->NumFiles; Lumps = new F7ZLump[NumLumps]; F7ZLump *lump_p = Lumps; TArray nameUTF16; TArray nameASCII; for (uint32_t i = 0; i < NumLumps; ++i) { // skip Directories if (SzArEx_IsDir(archPtr, i)) { skipped++; continue; } const size_t nameLength = SzArEx_GetFileNameUtf16(archPtr, i, NULL); if (0 == nameLength) { ++skipped; continue; } nameUTF16.Resize((unsigned)nameLength); nameASCII.Resize((unsigned)nameLength); SzArEx_GetFileNameUtf16(archPtr, i, &nameUTF16[0]); for (size_t c = 0; c < nameLength; ++c) { nameASCII[c] = static_cast(nameUTF16[c]); } FString name = &nameASCII[0]; name.Substitute("\\", "/"); name.ToLower(); lump_p->LumpNameSetup(name); lump_p->LumpSize = static_cast(SzArEx_GetFileSize(archPtr, i)); lump_p->Owner = this; lump_p->Flags = LUMPF_ZIPFILE|LUMPF_COMPRESSED; lump_p->Position = i; lump_p++; } // Resize the lump record array to its actual size NumLumps -= skipped; if (NumLumps > 0) { // Quick check for unsupported compression method TArray temp; temp.Resize(Lumps[0].LumpSize); if (SZ_OK != Archive->Extract(Lumps[0].Position, &temp[0])) { if (!quiet) Printf("\n%s: unsupported 7z/LZMA file!\n", FileName.GetChars()); return false; } } if (!quiet) Printf(", %d lumps\n", NumLumps); PostProcessArchive(&Lumps[0], sizeof(F7ZLump)); return true; } //========================================================================== // // // //========================================================================== F7ZFile::~F7ZFile() { if (Lumps != NULL) { delete[] Lumps; } if (Archive != NULL) { delete Archive; } } //========================================================================== // // Fills the lump cache and performs decompression // //========================================================================== int F7ZLump::FillCache() { Cache.Resize(LumpSize); static_cast(Owner)->Archive->Extract(Position, (char*)Cache.Data()); RefCount = 1; return 1; } //========================================================================== // // File open // //========================================================================== FResourceFile *Check7Z(const char *filename, FileReader &file, bool quiet) { char head[k7zSignatureSize]; if (file.GetLength() >= k7zSignatureSize) { file.Seek(0, FileReader::SeekSet); file.Read(&head, k7zSignatureSize); file.Seek(0, FileReader::SeekSet); if (!memcmp(head, k7zSignature, k7zSignatureSize)) { FResourceFile *rf = new F7ZFile(filename, file); if (rf->Open(quiet)) return rf; file = std::move(rf->Reader); // to avoid destruction of reader delete rf; } } return NULL; }