#include #include #include #include #include #include #include #include #include #include "ZipDefine.h" #include "CRC.h" #define INPUT_FILE_NAME "MyZip.txt" #define OUTPUT_FILE_NAME "MyZip.zip" static uint32_t RECURSIVE_COUNTER = 0; bool searchDir() { } bool FileReader(std::string strInputPath) { using namespace std; bool iRet = true; // path class obj 생성. filesystem::path pathObj(strInputPath); // file valid check if (!filesystem::exists(pathObj)) { string strDbg = "[ERROR] File not found : " + pathObj.string(); OutputDebugStringA(strDbg.c_str()); iRet = false; } enum class eFILETYPE { isFile, isDir, ETC }; // distinguish file type eFILETYPE eFileDist; if (filesystem::is_directory(pathObj)) eFileDist = eFILETYPE::isDir; else if (filesystem::is_regular_file(pathObj)) eFileDist = eFILETYPE::isFile; else eFileDist = eFILETYPE::ETC; switch (eFileDist) { // case 1 : input file case eFILETYPE::isFile: { ZipArchiver(pathObj.string()); break; } // case 2 : input directory case eFILETYPE::isDir: { // make iterator to traversal filesystem::directory_iterator itrDir(pathObj); while (itrDir != filesystem::end(itrDir)) { // get entry one by one const filesystem::directory_entry& currentEntry = *itrDir; if (filesystem::is_directory(currentEntry)) { RECURSIVE_COUNTER++; string strDbg = "[INFO] Current Entry (Dir)\t: " + currentEntry.path().string() + "\t Counter : " + to_string(RECURSIVE_COUNTER); OutputDebugStringA(strDbg.c_str()); // recursive traversal iRet = FileReader(currentEntry.path().string()); } else if (filesystem::is_regular_file(currentEntry)) { RECURSIVE_COUNTER++; string strDbg = "[INFO] Current Entry (File)\t: " + currentEntry.path().string() + "\t Counter : " + to_string(RECURSIVE_COUNTER); OutputDebugStringA(strDbg.c_str()); // archiving file ZipArchiver(currentEntry.path().string()); } else { // TODO : 근데 애초에 제 3의 Type 이 있나? (여기 의미 있는건가?) OutputDebugStringA("[ERROR] Wrong Type of File"); iRet = false; } // step forward entry itrDir++; } break; } // case 3 : ERROR case eFILETYPE::ETC: { // 이런 경우가 있으려나? 일단 혹시 모르니까 string strErr = "[ERROR] Something went Wrong..!!"; OutputDebugStringA(strErr.c_str()); iRet = false; //break; } default: break; } // TODO : 실패 상황 별 다른 코드 부여 + 코드 별 처리 어떻게 할 건지? return iRet; } bool ZipArchiver(std::string strInputPath) { LocalFileHeader LFHeader{}; CentralDirectory CentDir{}; EoCDRecord EOCDR{}; using namespace std; 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(strlen(INPUT_FILE_NAME)); 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; } int main() { std::printf("==================== Hello Zip! ====================\r\n"); int iRet = 0; // Main Zip archive method if (!FileReader()) { iRet = -1; std::printf("[ ERROR ] Somthing went Wrong... \r\n"); } else { std::printf("==================== Good Bye Zip! ====================\r\n"); } return iRet; }