게임을 실행하면 자동생성되는 부수적인 파일들이 있다. 게임의 옵션을 저장한 설정파일, shader 캐시 파일, 크래시 덤프 파일등…
Windows 7시절 까지도 많은 게임들이 게임의 exe가 있는 폴더에 그대로 저장했다. 물론 이는 MS의 권고사항이 아니다.
MS의 권고사항은 이렇게 폴더를 만들고 My Application폴더에 저장하는 것이다.
C:\Users\{user id}\AppData\Local\{My Application}
Windows 8부터는 이 권고사항이 사실상 강제되었다. 사용자가 Administrator 권한의 계정이라 해도(사실은 진짜 Administrator가 아니다) Program Files에 쓰기 권한이 제한된다.
따라서 게임을 Program Files폴더에 설치했다면 게임 데이터를 맘대로 써넣을 수 없게 된다.
꽤 호환성 이슈가 됐는데 그게 나중에 어떻게 처리됐는진 모르겠다. 많은 개발사들은 플레이어들이 게임을 실행할때 마우스 오른쪽 버튼을 눌러 Admin권한으로 게임을 실행하도록 권고했다.
하여간 난 그때 진행중이던/진행했던 모든 프로젝트를 MS의 권고사항을 따르는 쪽으로 고쳤다.
UWP(그 당시엔 WinRT)버전의 경우엔 어차피 패키지 폴더에 데이터 저장이 불가능했으므로 데스크탑 버전과 Windows Store버전 두가지를 다 지원하려면 처음부터 AppData/Local폴더에 저장하는 편이 좋으니까.
해서 VoxelHorizon 데스크탑버전은 다음의 위치에 데이터를 저장한다.
C:\Users\{user id}\AppData\Local\VoxelHorizon
UWP버전은 다음의 위치에 저장한다. 이쪽은 내가 임의로 지정하는게 아니고 앱 안에서 API를 통해서 path를 얻는다.
C:\Users\megay\AppData\Local\Packages\{App Package ID}\LocalState
하여간 이렇게 잘 쓰다가 요 근래부터 Desktop Bridge버전을 만들어서 배포하기 시작했다. Desktop Bridge라고 하는것은 기존 데스크탑 App을 UWP처럼 격리된 공간(프로세스 격리/파일 시스템 격리/레지스트리 격리)에서 실행하는 기술이다.
기존 데스크탑 앱은 .exe파일과 .dll파이들과 기타 등등 많은 파일들을 어딘가에 카피해놓고 .exe를 실행한다. Desktop Bridge로 패키징하면 이 모든 것들을 순수 UWP앱과 마찬가지로 AppX라는 파일 하나로 패키징 한다. 설치하면 임의의 격리된 저장소 위치에 이 파일들을 풀어서 카피한다.
기본적으로는 Desktop Bridge로 패키징하기 위해서 원래의 데스크탑앱의 코드를 수정해야할 필요는 없다. 이론상 그렇다. 실제로도 어지간하면 그냥 패키징만 해도 돌아는 간다. 물론 소소한 문제들은 발생할 수 있다.
그리고 그 소소한 문제중의 하나가 AppData/Local path 문제다.
위에서 언급했듯 VoxelHorizon의 데스크탑 버전은 C:\Users\{user id}\AppData\Local\VoxelHorizon 폴더에 데이터를 저장한다.
그러면 이걸 Desktop Bridge버전으로 패키징한후 게임을 실행해도 C:\Users\{user id}\AppData\Local\VoxelHorizon 폴더에 데이터를 저장할까?
생각해보자. 내가 지금 순수 데스크탑 버전의 Voxel Horizon을 설치해서 게임을 잘 플레이하고 있다. 이 상태에서 Desktop Bridge로 패키징한 Voxel Horizon을 추가로 설치했다. 그리고 게임을 실행했다. 그리고 이 Desktop Bridge버전이 C:\Users\{user id}\AppData\Local\VoxelHorizon폴더에 shader캐시와 게임 설정 파일등을 막 써넣는다.
그럼 내가 먼저 설치한 순수 데스크탑 버전의 파일들을 덮어 쓰겠네?
Desktop Bridge기술로 패키징하면 UWP앱처럼 완전한 sandbox로 돌아간다며? 그런데 global하게 시스템의 디스크에 막 데이터를 써넣어? 다른 어플리케이션의 데이터를 막 변경하고?
결론부터 얘기하면 그렇게 되지는 않는다.
애초에 Desktop Bridge앱은 그렇게 시스템의 저장소를 마구 억세스할 권한이 없다.
실제로 Desktop Bridge버전을 설치하고 shader캐시파일등이 어디에 저장됐는지 찾아보면 다음의 위치에 있다. 참고로 {App Apckage ID}는 패키징후에 나오는 패키지 아이디다. 앱이름이 Voxel Horizon이라 해도 패키지 아이디는 상당히 다른게 나온다.
C:\Users\{user id}\AppData\Local\Packages\{App Apckage ID}\LocalCache\Local\VoxelHorizon
Desktop Bridge로 패키징했을뿐, 파일 억세스 코드를 수정하진 않았다. 따라서 앱은 C:\Users\{user id}\AppData\Local\VoxelHorizon폴더에 데이터를 저장하려고 시도했다. 격리된 앱이므로 OS는 C:\Users\{user id}\AppData\Local\Packages\{App Apckage ID}\LocalCache\Local\VoxelHorizon 폴더를 대신 제공한 것이다.
OK. 좋다. 그럼 다음 주제로 넘어가자.
Voxel Horizon에는 게임이 크래시했을 경우 자동으로 덤프파일을 저장하는 기능이 들어있다. 이 덤프파일은 어디에 저장하는가? 당연히 AppData/Local폴더에 저장한다.
데스크탑버전은 C:\Users\{user id}\AppData\Local\VoxelHorizon폴더에, UWP버전은 C:\Users\megay\AppData\Local\Packages\{App Package ID}\LocalState폴더에 저장한다.
전에는 덤프파일을 저장한 후 자동으로 개발팀 FTP에 업로드 하도록 했다. 요새같으면 잡혀갈 일이다. 그래서 그렇게는 하지 않는다.
스팀에서 Project D Online을 팔때는 이런 수순으로 처리했다.
- 플레이어가 게임이 크래시했다고 메일을 보내거나 게시판에서 문의를 한다.
- xxx폴더를 열어서 그 안의 dmp파일과 systeminfo.txt파일을 메일로 보내주세요. 라고 유저한테 얘기한다.
- 메일로 받은 dmp파일과 txt파일을 분석한다.
순수 데스크탑 버전일때는 dmp파일 찾는게 비교적 쉽다. 물론 이것것도 유저들한테 설명하려면 간단하지 않다. 탐색기에서 hidden속성을 해제하는것부터 알려줘야 한다.
그런데 UWP버전이면 더더욱 dmp파일 찾는게 쉽지 않다.
그래서 콘솔명령어로 폴더를 바로 오픈하는 기능을 만들었다.
이와같이 커맨드를 입력하면 AppData 폴더를 열어준다.
자…문제는 Desktop Bridge버전이다.
open_user_folder커맨드를 입력하면 데스크탑 버전에선 C:\Users\{user id}\AppData\Local\VoxelHorizon폴더를 열어주려고 시도한다. Desktop Bridge로 패키징한다고 해서 이 코드가 자동으로 바뀌진 않는다.
그런데 Desktop Bridge버전에서 덤프파일을 생성하면 그건 어디로 가는가?
Desktop Bridge앱에서 C:\Users\{user id}\AppData\Local\VoxelHorizon폴더에 억세스 하려고 하면 OS가 C:\Users\{user id}\AppData\Local\Packages\{App Apckage ID}\LocalCache\Local\VoxelHorizon폴더를 돌려준다고 얘기했다.
따라서 덤프파일도 C:\Users\{user id}\AppData\Local\Packages\{App Apckage ID}\LocalCache\Local\VoxelHorizon폴더에 저장되어있다.
즉 Desktop Bridge버전에선 open_user_folder커맨드를 입력해봐야 보여줄 폴더가 없다. 그 폴더가 존재한다 해도 엉뚱한 폴더일 뿐이다.
올바른 실제 path 찾기
결국 약간의 코드 수정은 필요하다. 사실 Voxel Horizon의 데스크탑버전은 이미 Desktop Bridge로 작동하는지 여부를 판별해서 몇 가지 다른 작동을 하도록 되어있다. MS Live Account로그인도 그렇게 처리한다.
Desktop Bridge앱으로 작동할 경우 순수 UWP앱처럼 자신의 AppData/Local폴더를 API로 얻으면 간단하다. 그런데 유감스럽게도 이건 API를 못찾았다. 물론 있는데 내가 못찾은걸수도 있다. 패키지가 설치된 path는 GetCurrentPackagePath()함수를 사용하면 얻을 수 있다. 하지만 내가 원하는건 C:\Users\{user id}\AppData\Local\Packages\{App Apckage ID}\LocalCache\Local\VoxelHorizon 폴더의 위치다.
그래서 다음과 같이 처리했다.
-
- 일단 Desktop Bridge로 작동중이더라도 AppData/Local폴더는 C:\Users\{user id}\AppData\Local\VoxelHorizon라고 간주한다. 이하 AppData path로 부른다.
- Desktop Bridge로 작동하는 경우 AppData path에 아무 파일이나 하나 써넣는다. find_path.txt라는 이름으로 파일을 하나 써넣자.
- 격리된 파일 시스템으로 작동하므로 C:\Users\{user id}\AppData\Local\Packages\{App Apckage ID}\LocalCache\Local\VoxelHorizon\find_path.txt로 파일이 생성된다.
- 방금 써넣은 파일명 그대로 다시 파일을 연다(fopen()). 격리된 파일 시스템이므로 C:\Users\{user id}\AppData\Local\Packages\{App Apckage ID}\LocalCache\Local\VoxelHorizon\find_path.txt 파일이 열린다.
- 이 파일의 파일 포인터(FILE*)로 시스템상에서의 진짜 파일명을 얻는다. 파일 핸들로 파일의 full path를 얻는 방법은 다음의 링크에서 언급되어있다. 코드는 긁어다 붙여도 된다.
https://msdn.microsoft.com/ko-kr/library/windows/desktop/aa366789(v=vs.85).aspx
파일포인터를 HANDLE로 바꿔야하는데 다음과 같이 한다.
FILE* fp = nullptr;
_wfopen_s(&fp, “xxx.xxx”,”rb”);
int fn = _fileno(fp);
HANDLE hFile = (HANDLE)_get_osfhandle(fn);이렇게 시스템에서의 진짜 파일 이름을 얻으면 완전한 full path의 파일이름이 얻어진다. 여기서 path부분만 따서 C:\Users\{user id}\AppData\Local\VoxelHorizon로 설정한 처음의 AppData path를 대체하면 된다.