[C/C++] 파일 패킹

2022. 1. 6. 16:00C&C++

파일들을 하나의 파일로 묶고 푸는 예제.

 

패킹 파일은,

패킹 파일에 대한 헤더, 패킹된 파일들에 대한 헤더, 실제 패킹된 파일들의 데이터

이렇게 세 구간으로 구분된다.

 

패킹 파일에 대한 헤더는, 이 파일을 상징하는 iType과 묶인 파일들의 수를 저장한다.

패킹된 파일들에 대한 헤더는 각 패킹된 파일들의 파일 사이즈, 파일의 이름, 실제 데이터 시작 위치(offset)를 저장한다.

 

 이 때, offset을 저장하는 이유는

만약에 패킹 파일 내에서 변경(삭제 / 추가)이 일어났을 때, 중간에 데이터가 비는 부분이 발생할 수 있다.

예를 들어서, 중간 부분에 패킹된 100바이트 용량을 가진 파일이 삭제되고,

이 부분에 90바이트의 용량을 가진 파일이 추가된다면 사이즈로만 뒤의 파일들을 찾을 수 없다.

#include <iostream>
#define FILENAMELENGTH 128
#define PACKTYPE 0x99886655
//----------------------------------
// 패킹 파일 가장 앞 헤더
//----------------------------------
struct st_PACK_HEADER_
{
	unsigned int iType; // 0x99886655 이 들어감.
	int iFileNum;

};
//----------------------------------
// 내부 파일 정보
//----------------------------------
struct st_PACK_FILEINFO_
{
	int iFileSize;
	char szFileName[FILENAMELENGTH];
	int fileOffset;
};

int main()
{
	int i;
	int isPack;
	int packFileCount;
	char* fileNames;

	char packFileName[128];

	FILE* fp;
	FILE* fp2;
	//패킹 or 언패킹 선택
	printf("패킹 : 1, 언패킹 : 0 입력하세요 : ");
	scanf_s("%d", &isPack);
	//패킹
	if (isPack == 1)
	{
		//패킹할 파일 개수 입력받기
		printf("패킹할 파일의 개수를 입력하세요 : ");
		scanf_s("%d", &packFileCount);
		//각각의 파일 이름 입력 받기
		fileNames = (char*)malloc(packFileCount * FILENAMELENGTH);
		//예외처리
		if (fileNames == nullptr) exit(1);
		//inputstream 비우기
		rewind(stdin);
		for (i = 0; i < packFileCount; i++)
		{
			printf("%d 번째 파일 이름 입력: ", i + 1);
			gets_s(fileNames + i * FILENAMELENGTH, FILENAMELENGTH);
		}
		//패킹 파일 이름 입력받기
		printf("패킹 패일 이름 입력 : ");
		gets_s(packFileName, FILENAMELENGTH);
		//패킹하기
		//패킹 파일 생성
		fopen_s(&fp, packFileName, "wb");
		if (fp == nullptr) exit(1);
		//구조체 정보 입력
		st_PACK_HEADER_ fileHeader;
		fileHeader.iType = PACKTYPE;
		fileHeader.iFileNum = packFileCount;
		st_PACK_FILEINFO_* infoHeaders;
		infoHeaders = (st_PACK_FILEINFO_*)malloc(sizeof(st_PACK_FILEINFO_) * packFileCount);
		if (infoHeaders == nullptr) exit(1);

		char** filesPtr = (char**)malloc(packFileCount);
		if (filesPtr == nullptr) exit(1);
		char* fileContent;
		int fileSize;
		int fileOffset = sizeof(st_PACK_HEADER_) + packFileCount * sizeof(st_PACK_FILEINFO_);
		//파일 읽어오고 저장 반복
		for (i = 0; i < packFileCount; i++)
		{
			fopen_s(&fp2, fileNames + i * FILENAMELENGTH, "rb");
			if (fp2 == nullptr) exit(1);
			//파일 크기 찾기
			fseek(fp2, 0, SEEK_END);
			fileSize = ftell(fp2);
			fseek(fp2, 0, SEEK_SET);
			//해당 파일 내용 할당
			fileContent = (char*)malloc(fileSize);
			//예외처리
			if (fileContent == nullptr) exit(1);
			//읽기
			fread_s(fileContent, fileSize, 1, fileSize, fp2);
			//현재 파일 내용의 주소값 저장
			filesPtr[i] = fileContent;
			//현재 파일 헤더 구조체 저장
			infoHeaders[i].iFileSize = fileSize;
			infoHeaders[i].fileOffset = fileOffset;
			//다음 파일을 위해 오프셋 업데이트
			fileOffset += fileSize;
			strcpy_s(infoHeaders[i].szFileName, fileNames + i * FILENAMELENGTH);
			//파일 닫기
			fclose(fp2);
		}
		//패킹
		fwrite(&fileHeader, 1, sizeof(fileHeader), fp);
		fwrite(infoHeaders, 1, sizeof(st_PACK_FILEINFO_) * packFileCount, fp);
		for (i = 0; i < packFileCount; i++)
		{
			fwrite(filesPtr[i], 1, infoHeaders[i].iFileSize, fp);
		}

		//할당 해제
		for (i = 0; i < packFileCount; i++)
		{
			free(filesPtr[i]);
		}
		free(infoHeaders);
		free(filesPtr);
		free(fileNames);
		//파일 닫기
		fclose(fp);
	}
	//언패킹
	else
	{
		st_PACK_HEADER_ fileHeader;
		st_PACK_FILEINFO_ fileInfo;
		char* content;
		//스트림 비우기
		rewind(stdin);
		//패킹파일 입력받기
		printf("언패킹할 파일 이름 입력 : ");
		gets_s(packFileName, FILENAMELENGTH);
		//파일 열기
		fopen_s(&fp, packFileName, "rb");
		if (fp == nullptr) exit(1);
		//해당 파일 헤더 분석
		fread_s(&fileHeader, sizeof(fileHeader), 1, sizeof(fileHeader), fp);
		//타입확인 -> 아니면 오류
		if (fileHeader.iType != PACKTYPE)
		{
			exit(1);
		}
		//파일 개수 확인
		packFileCount = fileHeader.iFileNum;
		//각 파일 반복해서 뽑아내기
		for (i = 0; i < packFileCount; i++) 
		{
			//해당 파일 헤더 받기
			fseek(fp, sizeof(fileHeader) + i * sizeof(fileInfo), SEEK_SET);
			fread_s(&fileInfo, sizeof(fileInfo), 1, sizeof(fileInfo), fp);
			//파일의 오프셋으로 점프
			fseek(fp, fileInfo.fileOffset, SEEK_SET);
			//내용 뽑아내고 파일 생성
			//할당
			content = (char*)malloc(fileInfo.iFileSize);
			if (content == nullptr) exit(1);
			//읽기
			fread_s(content, fileInfo.iFileSize, 1, fileInfo.iFileSize, fp);
			//패킹 파일 분리
			//파일 생성
			fopen_s(&fp2, fileInfo.szFileName, "wb");
			if (fp2 == nullptr) exit(1);
			//파일에 쓰기
			fwrite(content, 1, fileInfo.iFileSize, fp2);
			//종료
			free(content);
			fclose(fp2);
		}
	}
	//종료
	return 0;
}

 

'C&C++' 카테고리의 다른 글

C++ 정리  (0) 2022.01.06
비트맵(.bmp) 파일 구조  (0) 2022.01.06
[C/C++] 파일 입출력  (0) 2022.01.06
C언어 문자열 처리  (0) 2022.01.06
C/C++ 시간을 측정하기  (0) 2022.01.06