diff --git a/MyZip.cpp b/MyZip.cpp new file mode 100644 index 0000000..7ff2b85 --- /dev/null +++ b/MyZip.cpp @@ -0,0 +1,139 @@ +#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; +} diff --git a/MyZip.h b/MyZip.h new file mode 100644 index 0000000..b2fae68 --- /dev/null +++ b/MyZip.h @@ -0,0 +1,45 @@ +#pragma once +#include +#include +#include +#include + +#include "ZipDefine.h" +#include "CRC.h" + +#define OUTPUT_FILE_NAME "MyZip.zip" + +class MyZip +{ +private: + std::ofstream fileOut; + std::string strOutputPath; + + // Zip Format struct + CentralDirectory CentDir{}; + EoCDRecord EOCDR{}; + + struct ArchivedEntry + { + CentralDirectory header; + std::string fileName; + }; + std::vector vecEntries; + +public: + MyZip(std::string strfilePath) + : strOutputPath(strfilePath) + { + fileOut = std::ofstream(strOutputPath, std::ios::binary); + CentDir.signature = PK_SIGNATURE_CENT_DIR; + EOCDR.signature = PK_SIGNATURE_EO_CDR; + + CentDir.versionMadeBy = 0; // Temp + CentDir.versionNeededToExtract = 0; // Temp + CentDir.generalBitFlag = 0; // Temp + CentDir.compressionMethod = METHOD_UNCOMPRESSED; + } + + bool AddFileToZip(const std::string &strFilePath); + bool FinalizeZip(); +}; diff --git a/MyZip.vcxproj b/MyZip.vcxproj index 3f48ed7..6b436d4 100644 --- a/MyZip.vcxproj +++ b/MyZip.vcxproj @@ -130,9 +130,11 @@ + + diff --git a/MyZip.vcxproj.filters b/MyZip.vcxproj.filters index b633e44..4812474 100644 --- a/MyZip.vcxproj.filters +++ b/MyZip.vcxproj.filters @@ -18,6 +18,9 @@ 소스 파일 + + 소스 파일 + @@ -26,5 +29,8 @@ 헤더 파일 + + 헤더 파일 + \ No newline at end of file diff --git a/main.cpp b/main.cpp index 536b44e..21f3368 100644 --- a/main.cpp +++ b/main.cpp @@ -1,145 +1,26 @@ -// 1. C++ 표준 라이브러리 (C 호환 및 C++ 표준을 알파벳 순으로 통합하거나 C/C++ 분리) #include #include +#include #include #include -#include -#include -#include #include -// 2. OS 전용 / 플랫폼 API (Windows) #include #include #include "ZipDefine.h" #include "CRC.h" - -#define INPUT_FILE_NAME "Target_Dir" -#define OUTPUT_FILE_NAME "MyZip.zip" +#include "MyZip.h" static uint32_t g_recursive_counter = 0; -bool ZipArchiver(std::string strInputPath, std::string strFileName) +bool ZipArchiver(std::string strInputPath) { - LocalFileHeader LFHeader{}; - CentralDirectory CentDir{}; - EoCDRecord EOCDR{}; + MyZip myZIP(strInputPath); - using namespace std; + myZIP.AddFileToZip(); - ifstream fileIn(strInputPath, ios::binary); - bool iRet = fileIn.is_open(); - - // 파일 크기 계산 - // TODO : Buffering 으로 구현. 현재는 파일 통짜로 읽음. - fileIn.seekg(0, ios::end); // seekg (offset, 목표점) - streamsize fileInSize = fileIn.tellg(); - fileIn.seekg(0, ios::beg); // beg == begin - - // 메모리 할당 - vector fileInBuffer(fileInSize); - auto* pInBuffer = reinterpret_cast(fileInBuffer.data()); - - // Read - fileIn.read(pInBuffer, fileInSize); - // ZIP 포맷 채워넣기 - // TODO : 클래스화 대상 - { - // Get CRC value - uint32_t myCRC = getCRC32(pInBuffer, fileInSize); - - // Put PK Signature - LFHeader.signature = PK_SIGNATURE_LF_HEADER; - CentDir.signature = PK_SIGNATURE_CENT_DIR; - EOCDR.signature = PK_SIGNATURE_EO_CDR; - - // TOOD : Temp Format 채우기. - CentDir.versionMadeBy = 0; // Temp - LFHeader.versionNeededToExtract = 0; // Temp - CentDir.versionNeededToExtract = 0; // Temp - LFHeader.generalBitFlag = 0; // Temp - CentDir.generalBitFlag = 0; // Temp - - // Put compressed method (uncompressed) - LFHeader.compressionMethod = METHOD_UNCOMPRESSED; - CentDir.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; - CentDir.sizeOfUncompressed = (uint32_t)fileInSize; - LFHeader.sizeOfCompressed = LFHeader.sizeOfUncompressed; - CentDir.sizeOfCompressed = CentDir.sizeOfUncompressed; - - // Put File name length - uint16_t fileNameLeng = static_cast(strFileName.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; - } - - // Zip arhcive 파일 쓰기 - { - ofstream fileOut(OUTPUT_FILE_NAME, 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(); - } - } - - return iRet; + return false; } bool FileReader(std::string strInputPath) @@ -174,7 +55,7 @@ bool FileReader(std::string strInputPath) // case 1 : input file case eFILETYPE::isFile: { - iRet = ZipArchiver(pathObj.string(), pathObj.filename().string()); + iRet = ZipArchiver(pathObj.string()); break; } @@ -211,7 +92,7 @@ bool FileReader(std::string strInputPath) OutputDebugStringA(strDbg.c_str()); // archiving file - iRet = ZipArchiver(currentPath.string(), currentPath.filename().string()); + iRet = ZipArchiver(currentPath.string()); } else {