/* ** 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. **--------------------------------------------------------------------------- ** ** */ #include "resourcefile.h" #include "cmdlib.h" #include "templates.h" #include "v_text.h" #include "w_zip.h" #include "i_system.h" #include "w_wad.h" extern "C" { #include "Archive/7z/7zHeader.h" #include "Archive/7z/7zExtract.h" #include "Archive/7z/7zIn.h" #include "7zCrc.h" } //----------------------------------------------------------------------- // // Interface classes to 7z library // //----------------------------------------------------------------------- extern ISzAlloc g_Alloc; 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; } }; //========================================================================== // // Zip Lump // //========================================================================== struct F7ZLump : public FResourceLump { int Position; virtual int FillCache(); }; //========================================================================== // // Zip file // //========================================================================== class F7ZFile : public FResourceFile { friend struct F7ZLump; F7ZLump *Lumps; C7zArchive *Archive; static int STACK_ARGS lumpcmp(const void * a, const void * b); 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; } }; int STACK_ARGS F7ZFile::lumpcmp(const void * a, const void * b) { F7ZLump * rec1 = (F7ZLump *)a; F7ZLump * rec2 = (F7ZLump *)b; return stricmp(rec1->FullName, rec2->FullName); } //========================================================================== // // 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" TEXTCOLOR_RED "%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 false; } NumLumps = Archive->DB.db.NumFiles; Lumps = new F7ZLump[NumLumps]; F7ZLump *lump_p = Lumps; for (DWORD i = 0; i < NumLumps; ++i) { CSzFileItem *file = &Archive->DB.db.Files[i]; // skip Directories if (file->IsDir) { skipped++; continue; } FString name = file->Name; FixPathSeperator(name); name.ToLower(); lump_p->LumpNameSetup(name); lump_p->LumpSize = int(file->Size); lump_p->Owner = this; lump_p->Flags = LUMPF_ZIPFILE; lump_p->Position = i; lump_p->CheckEmbedded(); 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); return false; } } if (!quiet) Printf(", %d lumps\n", NumLumps); // Entries in archives are sorted alphabetically qsort(&Lumps[0], NumLumps, sizeof(F7ZLump), lumpcmp); 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 = new char[LumpSize]; static_cast(Owner)->Archive->Extract(Position, Cache); 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, SEEK_SET); file->Read(&head, k7zSignatureSize); file->Seek(0, SEEK_SET); if (!memcmp(head, k7zSignature, k7zSignatureSize)) { FResourceFile *rf = new F7ZFile(filename, file); if (rf->Open(quiet)) return rf; rf->Reader = NULL; // to avoid destruction of reader delete rf; } } return NULL; }