WIN32API FrameWork/한단계씩 직접 구현

31. 이미지 로딩을 위한 준비 - 경로 관리자

hyrule 2022. 5. 24. 13:44

http://hyrule.tistory.com/111 

 

*** 공부 방법 ***

1. 코딩을 해야 하는 부분은 첫 부분에 변수나 함수, 메소드에 대한 선언이 코드블럭으로 표시되어 있다. //ex) MakeFunction(); 2. 코드블럭 하단에는 해당 선언에 대한 구현 로직이 작성되어 있다. 처

hyrule.tistory.com

 


 

[사전 지식]

TCHAR Root[MAX_PATH] = {};

GetModuleFileName(0, Root, MAX_PATH);

- MAX_PATH: 윈도우에서 기본으로 제공하는 매크로 -> 최대 경로 길이를 설정(260)

- GetModuleFileName

--1번 인자: 0을 넣어야 실행 파일의 주소가 반환된다.

--2번 인자: 실행 파일의 주소를 반환할 문자열의 주소

--3번 인자: 해당 문자열의 최대 길이

 

-- 이 주소를 통해 프로그램이 어느 곳에 있든 주소를 쉽게 구해올 수 있다.

-- 이 함수를 사용하면, "전체경로/프로그램_이름.exe" 문자열이 나오게 되는데, 경로만 사용하려면 반복문을 통해 프로그램 이름을 제거해주어야 한다.

--- 뒤에서부터 반복문을 돌려서 '/' 또는 '\\'를 찾을 경우 거기서 멈추고, 그 뒤를 memset을 통해 싹 날려버리면 된다.

 


int PathLength = WideCharToMultiByte(CP_ACP, 0, Info->Path, -1, nullptr, 0, 0, 0);

https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte

 

WideCharToMultiByte function (stringapiset.h) - Win32 apps

Maps a UTF-16 (wide character) string to a new character string.

docs.microsoft.com

- 유니코드를 멀티바이트로 바꿔주는 함수이다.

-1번 인자: CP_ACP -> 아스키 코드 페이지로 들어간다(고정)

-2번 인자: 플래그 -> 0(고정): 특정 조건에 해당하는 문자를 변경할 때 넣는 인자

-3번 인자: 바꿔줄 문자열 주소

-4번 인자: 바꿔줄 문자열의 개수 -> -1 = 전체를 바꿔주겠다

-5번 인자: 변환된 문자열을 넣을 주소

-> 5번 인자부터 0을 넣을 시 변환된 문자열의 길이(int)가 return됨.

-6번 인자: 변환된 문자열의 길이 -> 5번 인자에 nullptr을 넣어 반환된 값을 여기에 다시 넣어주면 된다.

 

▼사용 방법은 다음과 같다.

//길이를 먼저 리턴받은 다음,
int PathLength = WideCharToMultiByte(CP_ACP, 0, Info->Path, -1, nullptr, 0, 0, 0);

//리턴받은 길이를 다시 인자에 넣고 주소를 할당하여 변환을 완료한다.
WidCharToMultiByte(CP_ACP, 0, Info->Path, -1, Info->PathMultiByte, PathLength, 0, 0);

- 현재 컴파일 설정이 Unicode인지 Multibyte인지 구분하는 법

- 지난번에 사용했던 #ifdef 기능을 사용하면 된다.

#ifdef UNICODE
//원하는 코드//
#endif

- 프로그램은 어느 경로에 저장되어 있든 실행이 될 수 있어야 한다.

-- 프로그램은 이것을 '상대 경로'를 통해 처리하고 있다.

 

- 이미지 파일도 마찬가지로 프로그램이 어디에 있든 로드할 수 있어야 한다.

하지만 이미지 파일은 전체 경로가 필요하다. -> 매번 바뀌는 전체 경로를 따와서 로딩할 수 있어야 한다.

 

- 그러므로 이미지 파일을 처리하기 전에 경로를 관리해줄 클래스의 생성이 필요하다.


class CPathManager

- 루트 폴더에 생성한다. 

- 싱글턴 패턴으로 생성하고, CGameManager에서 생성, 초기화 및 삭제를 담당한다.


struct PathInfo

- 경로에 대한 지원은, 유니코드와 멀티바이트 모두를 지원해주어야 문제가 발생할 여지가 없다.

-- 그러므로 경로 저장은 구조체를 통해 저장한다.

-- 기본적으로 멀티바이트로 하나 저장하고,

-- 만약 유니코드면 유니코드 형식으로 저장되는 변수 타입을 통해 하나 더 저장한다.

--- 생성 시 0으로 초기화되도록 해준다. 

더보기
struct PathInfo
{
	TCHAR Path[MAX_PATH];
	char PathMultiByte[MAX_PATH];


	PathInfo():
		Path{},
		PathMultiByte{}
	{}
};

- 위 구조체를 모아서 저장할 자료구조를 생성한다. key로 문자열 이름을 저장하고, value로 PathInfo 구조체 포인터를 반환한다.

 

더보기
//PathManager.h

private:
	std::unordered_map<std::string, PathInfo*>	m_mapPath;

- 경로 이용의 편의를 위해, 기본 경로를 찾기 위한 문자열("RootPath")와,

텍스처 파일들의 경로를 찾기 위한 문자열("TexturePath")를

GameInfo.h에 매크로로 등록해 준다.

 

더보기
//GameInfo.h

//경로 매크로 설정
#define ROOT_PATH		"RootPath"
#define TEXTURE_PATH	"TexturePath"

 


CPathManager::FindPath()

- 인자로 이름(문자열)을 받아서

- PathInfo 구조체의 주소를 수정 불가능하게 return

 

- 위에 저장한 자료구조에서 인자로 받은 이름이 존재하는지 찾는다. 

-- 찾지 못했으면 nullptr을 리턴한다.

 

더보기
const PathInfo* CPathManager::FindPath(const std::string& Name)
{
	auto	iter = m_mapPath.find(Name);

	if (iter == m_mapPath.end())
		return nullptr;

	return iter->second;
}

CPathManager::AddPath()

<인자>

- 새로 만들 경로의 이름(문자열)

- 뒤에 추가할 경로(수정 불가능한 포인터)

- 앞에 추가할 기초경로를 찾기 위한 이름(문자열) -> 기본 인자로 ROOT_PATH를 전달.

 

<반환값>

- boolean

 

<로직>

- 위에서 만든 FindPath() 메소드를 활용.

- 만약 새로 만들 경로로 이미 이름이 있으면 false를 반환하고 return

- 앞에 추가할 기초경로를 이름으로 찾아서 임시로 저장

- PathInfo를 동적할당

- 기초 경로가 있다면 동적할당한 PathInfo에 해당 기초경로를 복사

- PathInfo에 복사된 기초경로에 '뒤에 추가할 경로'를 추가

- 추가가 완료되면 ifdef를 통해 멀티바이트로 바꾸는 작업을 진행

- 모든 단계가 성공적으로 끝날 시 true 반환

더보기
bool CPathManager::AddPath(const std::string& Name,
	const TCHAR* Path, const std::string& BasePathName)
{
	if (FindPath(Name))
		return false;

	const PathInfo* BasePath = FindPath(BasePathName);

	PathInfo* Info = new PathInfo;

	if (BasePath)
		lstrcpy(Info->Path, BasePath->Path);

	lstrcat(Info->Path, Path);

#ifdef UNICODE

	// 유니코드로 되어있는 문자열을 멀티바이트로 바꾸기 위한 수를
	// 얻어온다.
	int	PathLength = WideCharToMultiByte(CP_ACP, 0, Info->Path, -1,
		0, 0, 0, 0);

	WideCharToMultiByte(CP_ACP, 0, Info->Path, -1,
		Info->PathMultibyte, PathLength, 0, 0);

#else

	strcpy_s(Info->PathMultibyte, Info->Path);

#endif // UNICODE

	m_mapPath.insert(std::make_pair(Name, Info));

	return true;
}

CPath::Init()

- CPathManager의 초기화 과정에서 전체 경로에서 프로그램 이름만 뺀 경로를 구한다.

-- 해당 경로가 "RootPath"(ROOT_PATH)가 된다.

-- 해당 경로를 위의 자료구조에 첫 번째 원소로 저장한다.

 

- 위의 2가지 메소드들을 사용해 TEXTURE_PATH를 두 번째 원소로 등록해준다.

- "ROOT_PATH/Texture/"이 등록되어야 한다.

 

더보기
bool CPathManager::Init()
{
	TCHAR	Root[MAX_PATH] = {};

	// 실행파일이있는 폴더까지의 전체경로/실행파일이름.exe 로 문자열이
	// 나오게 된다.
	// Bin/aa.exe
	GetModuleFileName(0, Root, MAX_PATH);

	int	Length = lstrlen(Root);

	for (int i = Length - 1; i >= 0; --i)
	{
		if (Root[i] == '/' || Root[i] == '\\')
		{
			memset(&Root[i + 1], 0, sizeof(TCHAR) * (Length - i - 1));
			break;
		}
	}

	PathInfo* Info = new PathInfo;

	lstrcpy(Info->Path, Root);

	// #ifdef : 뒤에 있는 내용이 #define으로 정의되어 있는지를
	// 판단하는 if문이다. 
	// 컴파일 단계에서 뒤에 있는 내용이 #define으로 정의되어 있는지 판단.
#ifdef UNICODE

	// 유니코드로 되어있는 문자열을 멀티바이트로 바꾸기 위한 수를
	// 얻어온다.
	int	PathLength = WideCharToMultiByte(CP_ACP, 0, Info->Path, -1,
		0, 0, 0, 0); 
	
	WideCharToMultiByte(CP_ACP, 0, Info->Path, -1,
		Info->PathMultibyte, PathLength, 0, 0);

#else

	strcpy_s(Info->PathMultibyte, Info->Path);

#endif // UNICODE


	m_mapPath.insert(std::make_pair(ROOT_PATH, Info));

	AddPath(TEXTURE_PATH, TEXT("Texture/"));

	return true;
}

GameFrameworkStepbyStep_31_PathManager.zip
1.70MB