#include "MyZip.h" // Phase A(AddFile) / Phase B(Finalize) 구조: // - AddFile() : 파일 하나 읽어서 [LocalFileHeader][파일명][데이터]를 fileOut에 이어 쓰고, // 이 파일의 CentralDirectory 정보는 vecEntries에 누적만 해둠 (아직 안 씀). // - Finalize() : 전체 순회가 끝난 뒤 딱 한 번 호출. 누적된 CentralDirectory들을 순서대로 쓰고, // EOCD 작성 후 스트림을 닫음. bool MyZip::AddFileToZip(const std::string& strFilePath) { LocalFileHeader LFHeader{}; // - strFilePath 파일을 열어 크기 계산 + CRC 계산 // - LocalFileHeader 채워서 [헤더][파일명][데이터] 순서로 fileOut에 씀 // - 이 파일의 CentralDirectory를 채워서 vecEntries에 push_back // - 이 시점에는 CentralDirectory/EOCD를 파일에 쓰지 않음 (Finalize 몫) // strFilePath : 디스크 상의 실제 파일 경로 (FileReader가 넘겨주는 경로) // 3. LocalFileHeader 채우기 (signature/crc32/size/lengthOfFileName 등) // 4. fileOut에 [LocalFileHeader][파일명][데이터] 쓰기 // 5. 이 파일의 CentralDirectory 채워서 vecEntries.push_back({header, fileName}) std::ifstream fileIn(strFilePath, std::ios::binary); if (fileIn.is_open()) { return false; } // 파일 크기 계산 fileIn.seekg(0, std::ios::end); // seekg (offset, 목표점) std::streamsize fileInSize = fileIn.tellg(); fileIn.seekg(0, std::ios::beg); // beg == begin // 메모리 할당 std::vector fileInBuffer(fileInSize); // TODO : Buffering 으로 구현. 현재는 파일 통짜로 읽음. auto* pInBuffer = reinterpret_cast(fileInBuffer.data()); // Get CRC value uint32_t myCRC = getCRC32(pInBuffer, fileInSize); // ZIP 포맷 채워넣기 // Put PK Signature LFHeader.signature = PK_SIGNATURE_LF_HEADER; // TOOD : Temp Format 채우기. LFHeader.versionNeededToExtract = 0; // Temp LFHeader.generalBitFlag = 0; // Temp // Put compressed method (uncompressed) LFHeader.compressionMethod = METHOD_UNCOMPRESSED; // lastMode[Time|Date] => Not Used. // Put CRC value LFHeader.crc32 = myCRC; CentDir.crc32 = myCRC; // Put File size LFHeader.sizeOfUncompressed = (uint32_t)fileInSize; LFHeader.sizeOfCompressed = LFHeader.sizeOfUncompressed; CentDir.sizeOfUncompressed = (uint32_t)fileInSize; CentDir.sizeOfCompressed = CentDir.sizeOfUncompressed; // Put File name length uint16_t fileNameLeng = static_cast(strFilePath.length()); LFHeader.lengthOfFileName = fileNameLeng; CentDir.lengthOfFileName = fileNameLeng; // Extra Field 는 사용하지 않음 // LFHeader.lengthOfExtraField = NOT_USED; // CentDir.lengthOfExtraField = NOT_USED; // TODO : CentDir Temp format 채우기. CentDir.lengthOfFileComment = 0; CentDir.diskNumStart = 0; // Temp CentDir.attributeInternal = 0; // Temp CentDir.attributeExternal = 0; // Temp // CentDir.offsetLocalFileHeader -> 실제 파일 Write 중에 작성. // TODO : EOCDR Temp format 채우기. EOCDR.diskNumber = 0; // Temp EOCDR.diskNumber_CentralDirStart = 0; // Temp EOCDR.numOfCentralDir = 1; EOCDR.numOfEntries = 1; EOCDR.sizeOfCentralDir = sizeof(CentDir) + CentDir.lengthOfFileName; // EOCDR.offsetCentralDir -> 실제 파일 Write 중에 작성. EOCDR.lengthOfCommentLength = 0; // TODO : 성공여부 처리 어떻게 할 건지? return false; } // - vecEntries를 순서대로 순회하며 [CentralDirectory][파일명] 씀 // - EoCDRecord 채우기 (numOfEntries = vecEntries.size() 등 누적값 반영) // - EoCDRecord 쓰기 -> fileOut.close() // Zip arhcive 파일 쓰기 // EOCD 채우기 bool MyZip::FinalizeZip() { bool iRet = false; // TODO : 1. vecEntries 순서대로 [CentralDirectory][파일명] 쓰기 // 2. EoCDRecord 채우기 (entries 수, central dir 시작 오프셋/크기 등) // 3. EoCDRecord 쓰기 // 4. fileOut.close() std::ofstream fileOut(OUTPUT_FILE_NAME, std::ios::binary); iRet = fileOut.is_open(); if (!iRet) { printf("FileOut open failed"); } else { // 1. Local File Header, file name (var), extra field (var) CentDir.offsetLocalFileHeader = static_cast(fileOut.tellp()); // TODO : sizeof 의 값이 스펙과 정확히 일치한다는 보장 필요 (현재는 compiler 에 종속적) // : offsetof((LocalFileHeader, crc32) 으로 멤버별 static_assert 로 변경할 지 고민. // : 아니면 필드 단위 직접쓰기로 struct 분해하기? fileOut.write(reinterpret_cast(&LFHeader), sizeof(LFHeader)); fileOut.write(INPUT_FILE_NAME, LFHeader.lengthOfFileName); //fileOut.write(NOT_USED, LFHeader.LFHeader.lengthOfExtraField); // 2. File Data fileOut.write(pInBuffer, fileInSize); // 3. CentralDirectory, file name (var) EOCDR.offsetCentralDir = static_cast(fileOut.tellp()); // TODO : sizeof 의 값이 스펙과 정확히 일치한다는 보장 필요 (현재는 compiler 에 종속적) fileOut.write(reinterpret_cast(&CentDir), sizeof(CentDir)); fileOut.write(INPUT_FILE_NAME, CentDir.lengthOfFileName); // 4. End of CentralDirectory Record // TODO : sizeof 의 값이 스펙과 정확히 일치한다는 보장 필요 (현재는 compiler 에 종속적) fileOut.write(reinterpret_cast(&EOCDR), sizeof(EOCDR)); fileOut.close(); } // TODO : 성공여부 처리 어떻게 할 건지? return false; }