diff --git a/common/lc_library.cpp b/common/lc_library.cpp index 5a173a57..796e45ba 100644 --- a/common/lc_library.cpp +++ b/common/lc_library.cpp @@ -89,7 +89,7 @@ bool lcPiecesLibrary::OpenArchive(const char* FileName) { mZipFile = new lcZipFile(); - if (!mZipFile->Open(FileName)) + if (!mZipFile->OpenRead(FileName)) { delete mZipFile; mZipFile = NULL; @@ -146,7 +146,7 @@ bool lcPiecesLibrary::OpenArchive(const char* FileName) } else if (!memcmp(Name + 6, "P/", 2)) { - lcLibraryPrimitive* Prim = new lcLibraryPrimitive(Name + 8, FileIdx, (memcmp(Prim->mName, "STU", 3) == 0), false); + lcLibraryPrimitive* Prim = new lcLibraryPrimitive(Name + 8, FileIdx, (memcmp(Name + 8, "STU", 3) == 0), false); mPrimitives.Add(Prim); } } diff --git a/common/lc_zipfile.cpp b/common/lc_zipfile.cpp index b61b0976..50da57be 100644 --- a/common/lc_zipfile.cpp +++ b/common/lc_zipfile.cpp @@ -3,6 +3,7 @@ #include "lc_file.h" #include "lc_math.h" #include +#include lcZipFile::lcZipFile() { @@ -11,10 +12,11 @@ lcZipFile::lcZipFile() lcZipFile::~lcZipFile() { + Flush(); delete mFile; } -bool lcZipFile::Open(const char* FilePath) +bool lcZipFile::OpenRead(const char* FilePath) { lcDiskFile* File = new lcDiskFile(); mFile = File; @@ -29,6 +31,32 @@ bool lcZipFile::Open(const char* FilePath) return true; } +bool lcZipFile::OpenWrite(const char* FilePath) +{ + lcDiskFile* File = new lcDiskFile(); + mFile = File; + bool Exists = true; + + if (!File->Open(FilePath, "r+b") || !Open()) + { + Exists = false; + mNumEntries = 0; + mCentralDirSize = 0; + mCentralDirOffset = 0; + mBytesBeforeZipFile = 0; + mCentralPos = 0; + + if (!File->Open(FilePath, "wb")) + { + delete File; + mFile = NULL; + return false; + } + } + + return true; +} + lcuint64 lcZipFile::SearchCentralDir() { lcuint64 SizeFile, MaxBack, BackRead, PosFound; @@ -320,14 +348,16 @@ bool lcZipFile::ReadCentralDir() lcuint64 PosInCentralDir = mCentralDirOffset; mFile->Seek((long)(PosInCentralDir + mBytesBeforeZipFile), SEEK_SET); - mFiles.SetSize((int)mNumEntries); + mFiles.Expand((int)mNumEntries); for (lcuint64 FileNum = 0; FileNum < mNumEntries; FileNum++) { lcuint32 Magic, Number32; - lcZipFileInfo& FileInfo = mFiles[(int)FileNum]; + lcZipFileInfo& FileInfo = mFiles.Add(); long Seek = 0; + FileInfo.write_buffer = NULL; + if (mFile->ReadU32(&Magic, 1) != 1 || Magic != 0x02014b50) return false; @@ -689,3 +719,230 @@ bool lcZipFile::ExtractFile(int FileIndex, lcMemFile& File, lcuint32 MaxLength) return true; } + +bool lcZipFile::AddFile(const char* FileName, lcMemFile& File) +{ + const int BufferSize = 16384; + char WriteBuffer[BufferSize]; + z_stream Stream; + lcuint32 Crc32 = 0; + + File.Seek(0, SEEK_SET); + + Stream.zalloc = (alloc_func)0; + Stream.zfree = (free_func)0; + Stream.opaque = (voidpf)0; + + if (deflateInit2(&Stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) + return false; + + lcMemFile* OutFile = new lcMemFile(); + lcMemFile& CompressedFile = *OutFile; + + Bytef* BufferIn = File.mBuffer; + int Flush; + + do + { + uInt Read = lcMin(File.GetLength() - (BufferIn - File.mBuffer), BufferSize); + Stream.avail_in = Read; + Stream.next_in = BufferIn; + Crc32 = crc32(Crc32, BufferIn, Read); + BufferIn += Read; + + Flush = (BufferIn >= File.mBuffer + File.GetLength()) ? Z_FINISH : Z_NO_FLUSH; + + do + { + Stream.avail_out = BufferSize; + Stream.next_out = (Bytef*)WriteBuffer; + deflate(&Stream, Flush); + CompressedFile.WriteBuffer(WriteBuffer, BufferSize - Stream.avail_out); + } while (Stream.avail_out == 0); + } while (Flush != Z_FINISH); + + deflateEnd(&Stream); + + lcZipFileInfo& Info = mFiles.Add(); + + Info.version = 0; + if (CompressedFile.GetLength() >= 0xffffffff || File.GetLength() >= 0xffffffff) + Info.version_needed = 45; + else + Info.version_needed = 20; + Info.flag = 0; + Info.compression_method = Z_DEFLATED; + Info.crc = Crc32; + Info.compressed_size = CompressedFile.GetLength(); + Info.uncompressed_size = File.GetLength(); + Info.size_filename = strlen(FileName); + Info.size_file_extra = 0; + Info.size_file_comment = 0; + Info.disk_num_start = 0; + Info.internal_fa = 0; + Info.external_fa = 0; + Info.offset_curfile = 0; + strncpy(Info.file_name, FileName, sizeof(Info.file_name)); + + time_t rawtime; + struct tm* timeinfo; + + time(&rawtime); + timeinfo = localtime(&rawtime); + + Info.tmu_date.tm_sec = timeinfo->tm_sec; + Info.tmu_date.tm_min = timeinfo->tm_min; + Info.tmu_date.tm_hour = timeinfo->tm_hour; + Info.tmu_date.tm_mday = timeinfo->tm_mday; + Info.tmu_date.tm_mon = timeinfo->tm_mon ; + Info.tmu_date.tm_year = timeinfo->tm_year - 80; + Info.dosDate = (((timeinfo->tm_mday) + (32 * (timeinfo->tm_mon + 1)) + (512 * timeinfo->tm_year)) << 16) | ((timeinfo->tm_sec / 2) + (32 * timeinfo->tm_min) + (2048 * (uLong)timeinfo->tm_hour)); + + Info.write_buffer = OutFile; + + return true; +} + +void lcZipFile::Flush() +{ + int FirstFileAdded = -1; + + if (!mFile) + return; + + for (int FileIdx = 0; FileIdx < mFiles.GetSize(); FileIdx++) + { + lcZipFileInfo& FileInfo = mFiles[FileIdx]; + + if (FileInfo.write_buffer) + { + FirstFileAdded = FileIdx; + break; + } + } + + if (FirstFileAdded == -1) + return; + + lcuint64 CurrentOffset = mCentralDirOffset + mBytesBeforeZipFile; + mFile->Seek((long)CurrentOffset, SEEK_SET); + + void* CentralDir = malloc((long)mCentralDirSize); + mFile->ReadBuffer(CentralDir, (long)mCentralDirSize); + mFile->Seek((long)CurrentOffset, SEEK_SET); + + for (int FileIdx = FirstFileAdded; FileIdx < mFiles.GetSize(); FileIdx++) + { + lcZipFileInfo& FileInfo = mFiles[FileIdx]; + + FileInfo.offset_curfile = CurrentOffset; + + if (FileInfo.offset_curfile >= 0xffffffff) + FileInfo.version_needed = 45; + + mFile->WriteU32(0x04034b50); + mFile->WriteU16(FileInfo.version_needed); + mFile->WriteU16(FileInfo.flag); + mFile->WriteU16(FileInfo.compression_method); + mFile->WriteU32(FileInfo.dosDate); + mFile->WriteU32(FileInfo.crc); + if (FileInfo.compressed_size >= 0xffffffff) + mFile->WriteU32(0xffffffff); + else + mFile->WriteU32((lcuint32)FileInfo.compressed_size); + if (FileInfo.uncompressed_size >= 0xffffffff) + mFile->WriteU32(0xffffffff); + else + mFile->WriteU32((lcuint32)FileInfo.uncompressed_size); + mFile->WriteU16(FileInfo.size_filename); + mFile->WriteU16(FileInfo.size_file_extra); + mFile->WriteBuffer(FileInfo.file_name, FileInfo.size_filename); + + mFile->WriteBuffer(FileInfo.write_buffer->mBuffer, FileInfo.write_buffer->GetLength()); + delete FileInfo.write_buffer; + FileInfo.write_buffer = NULL; + + CurrentOffset = mFile->GetPosition(); + } + + lcuint64 CentralDirPos = mFile->GetPosition(); + mFile->WriteBuffer(CentralDir, (long)mCentralDirSize); + free(CentralDir); + + for (int FileIdx = FirstFileAdded; FileIdx < mFiles.GetSize(); FileIdx++) + { + lcZipFileInfo& FileInfo = mFiles[FileIdx]; + + mFile->WriteU32(0x02014b50); + mFile->WriteU16(FileInfo.version); + mFile->WriteU16(FileInfo.version_needed); + mFile->WriteU16(FileInfo.flag); + mFile->WriteU16(FileInfo.compression_method); + mFile->WriteU32(FileInfo.dosDate); + mFile->WriteU32(FileInfo.crc); + if (FileInfo.compressed_size >= 0xffffffff) + mFile->WriteU32(0xffffffff); + else + mFile->WriteU32((lcuint32)FileInfo.compressed_size); + if (FileInfo.uncompressed_size >= 0xffffffff) + mFile->WriteU32(0xffffffff); + else + mFile->WriteU32((lcuint32)FileInfo.uncompressed_size); + mFile->WriteU16(FileInfo.size_filename); + mFile->WriteU16(FileInfo.size_file_extra); + mFile->WriteU16(FileInfo.size_file_comment); + mFile->WriteU16(FileInfo.disk_num_start); + mFile->WriteU16(FileInfo.internal_fa); + mFile->WriteU32(FileInfo.external_fa); + + if (FileInfo.offset_curfile >= 0xffffffff) + mFile->WriteU32(0xffffffff); + else + mFile->WriteU32((lcuint32)(FileInfo.offset_curfile - mBytesBeforeZipFile)); + + mFile->WriteBuffer(FileInfo.file_name, FileInfo.size_filename); + } + + lcuint64 CentralDirEnd = mFile->GetPosition(); + + if (CentralDirPos - mBytesBeforeZipFile >= 0xffffffff) + { + mFile->WriteU32(0x6064b50); + mFile->WriteU64(44); + mFile->WriteU16(45); + mFile->WriteU16(45); + mFile->WriteU32(0); + mFile->WriteU32(0); + mFile->WriteU64(mFiles.GetSize()); + mFile->WriteU64(mFiles.GetSize()); + mFile->WriteU64(CentralDirEnd - CentralDirPos); + mFile->WriteU64(CentralDirPos - mBytesBeforeZipFile); + + mFile->WriteU32(0x7064b50); + mFile->WriteU32(0); + mFile->WriteU64(CentralDirEnd - mBytesBeforeZipFile); + mFile->WriteU32(1); + } + + mFile->WriteU32(0x06054b50); + mFile->WriteU16(0); + mFile->WriteU16(0); + + if (mFiles.GetSize() >= 0xffff) + mFile->WriteU16(0xffff); + else + mFile->WriteU16(mFiles.GetSize()); + + if (mFiles.GetSize() >= 0xffff) + mFile->WriteU16(0xffff); + else + mFile->WriteU16(mFiles.GetSize()); + + mFile->WriteU32((lcuint32)(CentralDirEnd - CentralDirPos)); + if (CentralDirPos - mBytesBeforeZipFile >= 0xffffffff) + mFile->WriteU32(0xffffffff); + else + mFile->WriteU32((lcuint32)(CentralDirPos - mBytesBeforeZipFile)); + + mFile->WriteU16(0); +} diff --git a/common/lc_zipfile.h b/common/lc_zipfile.h index 20ec8fb8..f977c4ec 100644 --- a/common/lc_zipfile.h +++ b/common/lc_zipfile.h @@ -38,6 +38,8 @@ struct lcZipFileInfo lcuint64 offset_curfile; // relative offset of local header 8 bytes char file_name[256]; tm_unz tmu_date; + + lcMemFile* write_buffer; }; class lcZipFile @@ -46,9 +48,13 @@ public: lcZipFile(); ~lcZipFile(); - bool Open(const char* FilePath); + bool OpenRead(const char* FilePath); + bool OpenWrite(const char* FilePath); + void Flush(); + bool ExtractFile(int FileIndex, lcMemFile& File, lcuint32 MaxLength = 0xffffffff); bool ExtractFile(const char* FileName, lcMemFile& File, lcuint32 MaxLength = 0xffffffff); + bool AddFile(const char* FileName, lcMemFile& File); ObjArray mFiles;