본문으로 바로가기

파일의 IT 블로그

  1. Home
  2. 프로그래밍/C
  3. [C] error: unknown type name 'NTSYSAPI' 해결하기 feat 다크소울 3 1.15용 FPS 해제 모드 제작

[C] error: unknown type name 'NTSYSAPI' 해결하기 feat 다크소울 3 1.15용 FPS 해제 모드 제작

· 댓글개 · KRFile
error: unknown type name 'NTSYSAPI'

C언어에서 윈도우 API 가 포함된 코드를 빌드하니 다음과 같은 오류가 발생했습니다.

컴파일러는 이전에 설치해둔 MinGW라는 것입니다. 인터넷에 검색해도 오늘날 윈도우32 API를 순수 코딩에 사용하는 사람은 거희 없어서 관련 정보를 찾기가 매우 매우 어려웠습니다. 오늘은 이 오류를 해결해본 삽질의 기록입니다.


 

이전에 다크소울 3 60프레임 해제하는 모드를 올렸습니다. DS3DebugFPS라는 것인데 기존에 다크소울 3 치트엔진 테이블 (ct 파일) 에 60프레임 제한을 푸는 기능이 존재했는데 그걸 C언어로 옮겨와서 편하게 사용할 수 있도록 개발된 프로그램입니다. 

 

https://dotobi-game.tistory.com/846

 

다크 소울 3 모드 | 신더 모드 적용법

다크 소울 3 추천 모드 소개. 신더 모드(Cinders)는 다크 소울 3에 신선한 경험을 제공하는 것을 목표로 합니다. 이는 전체적으로 정비하며, 사실상 모든 것이 수정되었음을 의미합니다. 정확한 세

dotobi-game.tistory.com

다크소울 3 신더모드라고.. 나름 대형모드가 있는데 이 모드 한글 패치가 전무해서 최근 들어 작업하고 있었는데 버전이 1.15 버전만 호환된다고 하더군요. 60프레임 해제 모드는 1.15.1 이랑 1.15.2 밖에 없는데 ㅡㅡ

 

당연히 1.15.1 이나 1.15.2 의 프레임 해제 DLL이 다크소울 1.15 버전에서는 작동하지 않았습니다.

 

DS3DebugFPS Issue 탭에 가서 1.15 버전용으로 만들어달라고 할 시간에 제가 만들게 빠를거 같아서 소스코드 복사해오고 바로 작업 시작했습니다.

 

일단 다크소울 1.15 용 치트엔진 테이블 ct파일을 구했습니다.

 

스크립트를 더블 클릭해서 열어보니 역시 다양한 변수의 Offset이 미리 정의되어 있습니다.

최신 버전으로 오면선 AOB Scan으로 방식이 바뀌어서 고정 주소 찾기가 어려운데 구버전의 다크소울 3 CT 파일들은 이렇게 고정으로 오프셋을 박아놨더라구요.

 

결론적으로 오늘 FPS 작업에 필요한 포인터 변수는 바로 저 SprjFlipper 이라는 놈 입니다. 사실 이름의 의미는 전혀 모르겠네요. 어쨌던 다크소울 3 exe가 실행하면 올라오는 주소 + 4888440 에 포인터 변수가 있다고 합니다.

16진수 오프셋 값 4888440 를 미리 메모해 둡니다.

 

저는 dll 파일을 패치할 것이므로 소스코드 중에서 dll에 먼저 들어갔습니다.

사실은 exe 형식의 것을 먼저 작업했는데 실패했습니다.

분명 똑같은 방식으로 작업했는데도 dll 은 되고 exe 는 작업이 안되더라구요;; 참 이상합니다

 

.macro loadnjmp sym
.section .text
.global \sym
\sym:
pushq %r9
pushq %r8
pushq %rdx
pushq %rcx
subq $0x28,%rsp
call LoadGenuineDll
addq $0x28,%rsp
popq %rcx
popq %rdx
popq %r8
popq %r9
jmp *\sym\()_(%rip)
.section .drectve
.ascii "-export:\sym\n"
.endm

.section .text
loadnjmp D3DAssemble
loadnjmp DebugSetMute
loadnjmp D3DCompile
loadnjmp D3DCompressShaders
loadnjmp D3DCreateBlob
loadnjmp D3DDecompressShaders
loadnjmp D3DDisassemble
loadnjmp D3DDisassemble10Effect
loadnjmp D3DGetBlobPart
loadnjmp D3DGetDebugInfo
loadnjmp D3DGetInputAndOutputSignatureBlob
loadnjmp D3DGetInputSignatureBlob
loadnjmp D3DGetOutputSignatureBlob
loadnjmp D3DPreprocess
loadnjmp D3DReflect
loadnjmp D3DReturnFailure1
loadnjmp D3DStripShader
#include "dllmain.h"
#include "../../external/inih/ini.c"

typedef struct {
  float fps;
  int UseCustomScreenDimensions;
  int ScreenWidth;
  int ScreenHeight;
  int EnableCursorClip;
  int CursorClipHotkey;
} config;
config configFile;

void containCursor(void *args) {
  while (TRUE) {
    if (GetAsyncKeyState(configFile.CursorClipHotkey)) {
      if (GetForegroundWindow() == args)
        SetCapture(args);
      ClipCursor(&final);
    }
  }
}

static int handler(void *Settings, const char *section, const char *name, const char *value) {
  config *modconfig = (config *)Settings;
  if (MATCH("Settings", "fps")) {
    modconfig->fps = atof(value);
  }
  if (MATCH("Settings", "UseCustomScreenDimensions")) {
    modconfig->UseCustomScreenDimensions = atoi(value);
  } else if (MATCH("Settings", "ScreenWidth")) {
    modconfig->ScreenWidth = atoi(value);
  } else if (MATCH("Settings", "ScreenHeight")) {
    modconfig->ScreenHeight = atoi(value);
  } else if (MATCH("Settings", "EnableCursorClip")) {
    modconfig->EnableCursorClip = atoi(value);
  } else if (MATCH("Settings", "CursorClipHotkey")) {
    modconfig->CursorClipHotkey = strtol(value, NULL, 16);
  } else
    return 0;
  return 1;
}

float readFile() {
  return ini_parse("FPSconfig.ini", handler, &configFile) < 0 ? 1000 : configFile.fps; // will set framerate limit to 1000 if the ini file is missing
}

void setFps(float rFPS) {

  // Find Process
  DWORD PID;
  HWND hWnd = FindWindowA(NULL, "DARK SOULS III");
  GetWindowThreadProcessId(hWnd, &PID);
  HANDLE pHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);

  // Skip Intros 
  unsigned char SkipIntro[20] = {0x48, 0x31, 0xC0, 0x48, 0x89, 0x02, 0x49, 0x89, 0x04, 0x24, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
  NtWriteVirtualMemory(pHandle, (LPVOID)0x140BF66AE, SkipIntro, 20, 0);

  // Borderless
  if (configFile.UseCustomScreenDimensions != 1) {
    final.right = GetSystemMetrics(SM_CXSCREEN);
    final.bottom = GetSystemMetrics(SM_CYSCREEN);
  } else {
    final.right = configFile.ScreenWidth;
    final.bottom = configFile.ScreenHeight;
  }
  final.left = 0;
  final.top = 0;
  SetWindowLong(hWnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
  AdjustWindowRect(&final, GetWindowLong(hWnd, GWL_STYLE), FALSE);
  SetWindowLong(hWnd, GWL_EXSTYLE, (GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_TOPMOST));
  MoveWindow(hWnd, final.left, final.top, final.right - final.left, final.bottom - final.top, TRUE);

  SprjFlipper = (DWORD64)GetModuleHandle("darksoulsiii.exe") + 0x489DD10;
  
  // GRAPHICS -> GFX
  NtReadVirtualMemory(pHandle, (LPVOID)SprjFlipper, &SprjFlipper, sizeof(SprjFlipper), NULL);

  // Debug FPS | デバッグFPS
  NtWriteVirtualMemory(pHandle, (LPVOID)(SprjFlipper + 0x354), &rFPS, sizeof(DWORD), NULL);

  // Use Debug FPS | デバッグFPSを利用するか
  NtWriteVirtualMemory(pHandle, (LPVOID)(SprjFlipper + 0x358), &useDebug, sizeof(char), NULL);

  if (configFile.EnableCursorClip != 0) {

    // createthread for an asynchronous loop
    HANDLE thread = CreateThread(NULL, 0, (void *)containCursor, &hWnd, 0, NULL);
  }
}

HINSTANCE BaseAddress, BaseAddressGenuine;
wchar_t *BaseFileName, FullFilePath[512];
BOOL WINAPI DllMain(HINSTANCE baseaddr, DWORD reason, BOOL isstatic) {
  switch (reason) {
  case DLL_PROCESS_ATTACH:
    BaseFileName = FullFilePath + GetModuleFileNameW(baseaddr, FullFilePath, _countof(FullFilePath));
    while (BaseFileName-- > FullFilePath)
      if (*BaseFileName == L'\\')
        break;
  case DLL_THREAD_ATTACH:
    break;
  }
  return 1;
}

importD3D(FARPROC D3DAssemble_, DebugSetMute_, D3DCompile_, D3DCompressShaders_, D3DCreateBlob_, D3DDecompressShaders_, D3DDisassemble_, D3DDisassemble10Effect_, D3DGetBlobPart_, D3DGetDebugInfo_, D3DGetInputAndOutputSignatureBlob_, D3DGetInputSignatureBlob_,
          D3DGetOutputSignatureBlob_, D3DPreprocess_, D3DReflect_, D3DReturnFailure1_, D3DStripShader_) void LoadGenuineDll() {
  if (!BaseAddressGenuine) {
    static wchar_t filename[512];
    GetSystemDirectoryW(filename, _countof(filename));
    BaseAddressGenuine = LoadLibraryW(wcscat(filename, BaseFileName));
    IMPORT(D3DAssemble);
    IMPORT(DebugSetMute);
    IMPORT(D3DCompile);
    IMPORT(D3DCompressShaders);
    IMPORT(D3DCreateBlob);
    IMPORT(D3DDecompressShaders);
    IMPORT(D3DDisassemble);
    IMPORT(D3DDisassemble10Effect);
    IMPORT(D3DGetBlobPart);
    IMPORT(D3DGetDebugInfo);
    IMPORT(D3DGetInputAndOutputSignatureBlob);
    IMPORT(D3DGetInputSignatureBlob);
    IMPORT(D3DGetOutputSignatureBlob);
    IMPORT(D3DPreprocess);
    IMPORT(D3DReflect);
    IMPORT(D3DReturnFailure1);
    IMPORT(D3DStripShader);
    setFps(readFile());
  }
}

어쨌던 dll 폴더에서 관련 로딩을 위한 어셈블리 코드와 main C언어 코드가 있는걸 볼 수 있습니다.

어셈블리 코드는 쭈욱 읽어봤는데 dll 로딩에 관여하는거라 전부 건들필요가 없고 결론적으로 C언어 코드만 만지면 됐습니다. Github에서 어셈블리 코드를 다운 받는 날이 올줄이야 ㅋㅋㅋㅋ...;;

 

  SprjFlipper = (DWORD64)GetModuleHandle("darksoulsiii.exe") + 0x489DD10;
  
  // GRAPHICS -> GFX
  NtReadVirtualMemory(pHandle, (LPVOID)SprjFlipper, &SprjFlipper, sizeof(SprjFlipper), NULL);

  // Debug FPS | デバッグFPS
  NtWriteVirtualMemory(pHandle, (LPVOID)(SprjFlipper + 0x354), &rFPS, sizeof(DWORD), NULL);

  // Use Debug FPS | デバッグFPSを利用するか
  NtWriteVirtualMemory(pHandle, (LPVOID)(SprjFlipper + 0x358), &useDebug, sizeof(char), NULL);

코드가 되게 긴데 사실 여기 4줄 정도만 보면 됩니다.

위에 SprjFlipper 가 FPS Unlock 에 관여하는 포인터이고 오른쪽에 아까 찾아 놓은 오프셋을 입력해야 할 것 처럼 보이네요. 저는 지금 최신 버전의 DS3Debug를 가져와서 1.15.2 를 기준으로 오프셋은 0x489DD10 이지만 아까 CT를 보셨다 싶이 1.15용은 4888440 였습니다. 저거만 수정해주면 끝날 거 같네요.

 

  // GRAPHICS -> GFX
  NtReadVirtualMemory(pHandle, (LPVOID)SprjFlipper, &SprjFlipper, sizeof(SprjFlipper), NULL);

  // Debug FPS | デバッグFPS
  NtWriteVirtualMemory(pHandle, (LPVOID)(SprjFlipper + 0x354), &rFPS, sizeof(DWORD), NULL);

  // Use Debug FPS | デバッグFPSを利用するか
  NtWriteVirtualMemory(pHandle, (LPVOID)(SprjFlipper + 0x358), &useDebug, sizeof(char), NULL);

그리고 NtReadVirtualMemory / NtWriteVirtualMemory 부분.

저게 바로 치트엔진에서 사용하던 메모리 읽기, 쓰기를 구현하는 부분입니다.

 

찾아보니 각각 프로세스에 메모리를 읽기 / 쓰기하는 함수네요. 다만 검색했을 때 윈도우 MSDN 공식 문서는 안나오는걸로 봐서 윈도우 32 API는 아닌 거 같고 ntdll 안에 있는 함수같은데 잘 모르겠네요 ㅈㅅ 제가 윈도우 커널구조는 공부를 안해봐서 잘 모릅니다. 유닉스 커널구조만 조금 암 ㅎ;

 

  // Use Debug FPS | デバッグFPSを利用するか
  NtWriteVirtualMemory(pHandle, (LPVOID)(SprjFlipper + 0x358), &useDebug, sizeof(char), NULL);

사실 저게 뭐하는 코드냐 생각할 수 있는데 사실 치트엔진에서 하던 걸 그냥 그대로 따라하는 겁니다. 치트엔진에서 클릭해서 번거롭게 사용했던 기능들은 사실 간략화(추상화) 시켜보면 메모리를 그냥 읽고 쓰는 동작이라 그걸 함수를 가져와서 구현한 것 뿐이죠.

 

SprjFlipper = (DWORD64)GetModuleHandle("darksoulsiii.exe") + 0x4888440; // 1.15 에서만 작동

설명이 졸라 길었는데 그냥 바꾼건 이게 끝입니다 ㅋㅋㅋㅋ

오프셋만 바꿔주세요.

 

@gcc -o D3DCompiler_43.dll asm.s dllmain.c -O3 -lmsvcrt -lntdll -shared -nostartfiles -Wno-multichar -Wl,--image-base,0x150000000,-e,DllMain
@pause

어랏. 근데 문제가 발생했습니다 

빌드를 하려고 보니깐 빌드에 관련된 설명이 Github에 단 한글자도 포함 되어 있지 않네요

사실 Star가 그렇게 많은 프로젝트도 아니고 잘 알려진 프로젝트도 아니라 그런 거 같습니다

build.bat 파일에 떨렁 gcc 명령어가 한 줄 적혀있는데 이게 빌드 명령어인가봐요

 

깃허브에서 파일을 받아오는건 원숭이도 할 수 있을만큼 쉽지만 빌드를 하거나 동작시킬 수 있게끔 만드는 일은 MIT 공대 석박사가 와도 어렵다. 라는 말이 있습니다 (사실 방금 제가 만든 말이지만 팩트입니다)

 

 

PS F:\Download\DS3DebugFPS-master\DS3DebugFPS-master\src\exe> .\build.bat
In file included from main.c:1:0:
main.h:13:1: error: unknown type name 'NTSYSAPI'
 NTSYSAPI NTSTATUS NTAPI NtReadVirtualMemory(HANDLE ProcessHandle, void *BaseAddress, const void *Buffer,
 ^~~~~~~~
main.h:13:1: warning: '__stdcall__' attribute only applies to function types [-Wattributes]
main.h:13:25: error: expected ',' or ';' before 'NtReadVirtualMemory'
 NTSYSAPI NTSTATUS NTAPI NtReadVirtualMemory(HANDLE ProcessHandle, void *BaseAddress, const void *Buffer,
                         ^~~~~~~~~~~~~~~~~~~
main.h:16:1: error: unknown type name 'NTSYSAPI'
 NTSYSAPI NTSTATUS NTAPI NtWriteVirtualMemory(HANDLE ProcessHandle, void *BaseAddress, const void *Buffer,
 ^~~~~~~~
main.h:16:1: warning: '__stdcall__' attribute only applies to function types [-Wattributes]
main.h:16:25: error: expected ',' or ';' before 'NtWriteVirtualMemory'
 NTSYSAPI NTSTATUS NTAPI NtWriteVirtualMemory(HANDLE ProcessHandle, void *BaseAddress, const void *Buffer,
                         ^~~~~~~~~~~~~~~~~~~~
main.c: In function 'setFps':
main.c:74:17: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
   SprjFlipper = (DWORD64)GetModuleHandle("darksoulsiii.exe") + 0x4888440; // 1.15 ?먯꽌留??묐룞
                 ^
main.c:77:3: warning: implicit declaration of function 'NtReadVirtualMemory' [-Wimplicit-function-declaration]
   NtReadVirtualMemory(pHandle, (LPVOID)SprjFlipper, &SprjFlipper, sizeof(SprjFlipper), NULL);
   ^~~~~~~~~~~~~~~~~~~
main.c:77:32: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
   NtReadVirtualMemory(pHandle, (LPVOID)SprjFlipper, &SprjFlipper, sizeof(SprjFlipper), NULL);
                                ^
main.c:80:3: warning: implicit declaration of function 'NtWriteVirtualMemory' [-Wimplicit-function-declaration]
   NtWriteVirtualMemory(pHandle, (LPVOID)(SprjFlipper + 0x354), &rFPS, sizeof(DWORD), NULL);
   ^~~~~~~~~~~~~~~~~~~~
main.c:80:33: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
   NtWriteVirtualMemory(pHandle, (LPVOID)(SprjFlipper + 0x354), &rFPS, sizeof(DWORD), NULL);
                                 ^
main.c:83:33: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
   NtWriteVirtualMemory(pHandle, (LPVOID)(SprjFlipper + 0x358), &useDebug, sizeof(char), NULL);
                                 ^

시원하게 빌드를 갈기니깐 에러 1개와 엄청난 오류 메세지를 만났습니다.

 

main.h:13:1: error: unknown type name 'NTSYSAPI'
 NTSYSAPI NTSTATUS NTAPI NtReadVirtualMemory(HANDLE ProcessHandle, void *BaseAddress, const void *Buffer,
 ^~~~~~~~

사실 Warning 이야 그냥 타입 안맞아서 암시적 형변환 일어날거라고 경고주는거라 무시하고 일단 이 오류가 젤 문제입니다. NtReadVirtual Memory 앞에 적혀있는 NTSYSAPI 란 것이 없답니다

 

 

Github에 허겁지겁 검색해봤는데 아무것도 안나오네요 ㅅㅂ

운이 좋게도 이 문제를 경험한 사람이 제가 최초인가봅니다!! 역시 저는 운이 좋습니다

 

일단 빌드 메세지를 확인해본 결과 오류는 죄다 헤더 파일에서 나는 문제였습니다. 오류는 헤더 파일 1개에서 또 그 오류마저도 단 1개. 

 

그러나 이상했던 건 VSCode의 타입 분석기능이 제대로 동작하고 있다는 것이었습니다.

NTSYSAPI 에 마우스를 올려보니 #define 매크로라고 제대로 분석을 하는 모습입니다

Ctrl + 클릭을 통해 어디에 저게 정의되어 있는지 확인해봅시다

 

NTSYSAPI는 winnt.h 라는 파일에 정의되어 있었으며 C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\um 라는 경로에 있었습니다. Windows Kits 가 뭔지 알아보니 Windows SDK 라는 도구라고 합니다.

 

윈도우 SDK
Windows 11 버전 22H2용 Windows SDK(10.0.22621)는 Windows 애플리케이션 빌드를 위한 최신 헤더, 라이브러리, 메타데이터 및 도구를 제공합니다. 이 SDK를 사용하여 Windows 11, 버전 22H2 및 이전 Windows 릴리스용 UWP(유니버설 Windows 플랫폼) 및 Win32 애플리케이션을 빌드합니다.
https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/

Visual Studio 설치할 때 선택에서 설치한 거 같은데 윈도우 개발 시 필요한 헤더 파일 및 라이브러리를 모아둔 설치파일 인 것 같네요.

 

사실 이때부터 이상함을 느꼈습니다. 원래 윈도우 API 프로그래밍 하려면 웬만해서 windows.h 라이브러리만 Include 해도 필요한게 전부 딸려와서 바로 작업을 할 수 있거든요. 혹시 몰라서 #include <winnt.h> 를 추가하고 빌드했으나 똑같은 오류로 실패했습니다.

 

참고로 #include 시 < > 와 같이 괄호를 사용해서 추가하는 건 현재 컴파일러의 표준 라이브러리 폴더에 있는 표준 라이브러리를 가져와서 쓰겠다는 뜻 입니다. 일반적으로 컴파일러 폴더의 include 폴더에 표준 라이브러리가 모두 모여있습니다. 

뭔가 winnt.h 가 제대로 include 되고 있는 거 같지 않아서 " " 를 이용해 절대 경로로 include 해줬습니다.

 

괄호가 아닌 따옴표를 사용하면 표준 라이브러리가 아니라 유저가 만든 헤더 파일을 include 하겠다는 뜻이 됩니다.

저렇게 해도 오류가 났습니다. 오류 메세지는 winnt.h 에서 불러오는 표준 라이브러리를 찾을 수 없다는 오류였습니다.

 

결론적으로 표준 라이브러리를 불러오면서 문제가 있다는 뜻이였고 이건 제 환경에 설치된 GCC 컴파일러의 문제라는 결론이 나게 되었습니다.

 

gcc.exe (MinGW.org GCC-6.3.0-1) 6.3.0
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

왜 확실하게 알게 됐냐면 제가 MinGW라는 곳에서 MinGW Installer 란 것으로 GCC 컴파일러를 받았는데 제가 gcc 컴파일러 받아놓은 C드라이브 MinGW 경로 include 폴더를 찾아보니 winnt.h 에 NTSYSAPI 가 실제로 존재하지 않았습니다.

 

또 업데이트를 걸었는데도 6.3.0 에서 더이상 버전 업이 되지 않더라구요.

 

그러니깐 VSCode에서는 제가 경로 설정을 잘못 해놔서 표준 라이브러리 경로가 C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\um 인 Windows SDK 경로로 잡혀 있었고, 실제 컴파일하는 GCC 경로에는 헤더 파일에 NTSYSAPI 가 없었던 것입니다.

 

설명이 너무 어려웠는데 NTSYSAPI 를 인식하지 못한 이유는 제가 구버전의 GCC 컴파일러를 사용하기 때문이므로 GCC 컴파일러를 새로운 곳에서 다시 받고, VSCode에서 표준 라이브러리 경로를 제가 GCC 컴파일러를 받아둔 경로로 새로 수정해주면 될 일 이였습니다.

 

GCC 컴파일러 새버전으로 다시 받기

혹시나 몰라서 위 설치 파일로 GCC 컴파일러를 다시 받아봤는데 설치가 제대로 되지 않더라구요.

 

https://www.mingw-w64.org/

 

MinGW-w64

 

www.mingw-w64.org

https://github.com/niXman/mingw-builds-binaries

 

GitHub - niXman/mingw-builds-binaries: MinGW-W64 compiler binaries

MinGW-W64 compiler binaries. Contribute to niXman/mingw-builds-binaries development by creating an account on GitHub.

github.com

 

GCC를 배포하는 공홈에 가보니 기본 SourceForge 에서 배포하던게 Github 링크로 바뀌어 있었습니다.

 

Github에서 뿌리는 Online Installer 로 다시 받아줍니다.

 

설치 인터페이스가 완전히 바뀌었습니다. Next를 눌러서 진행해줍시다

 

제가 글 쓴 기준으로 최신 버전은 13.1.0 이네요. 제가 쓰는 버전 MinGW 6.3.0 버전이 문제의 원흉이였습니다 ㅡㅡ

 

플랫폼은 64bit로 쓰레드 모델은 posix랑 win32 중에서 win32를 골라주면 될 거 같습니다.

인터넷 글들 보면 64bit나 32bit로 설치하고 posix 로 윈도우에 쓰레드 모델을 선택하던데 사실 왜 그러는지 잘 모르겠습니다. 유닉스 시스템에서 사용하는 쓰레드 모델 pthread는 윈도우 32 API에서 사용하는 쓰레드 모델하고 호환이 안되는걸로 아는데요..

 

윈도우 시스템에서 쓰레드 생성하려면 CreateThread 윈도우 API로 생성해야 하는걸로 아는데 유닉스 시스템 쓰레드 모델인 posix 를 왜 윈도우에 설치하라고 하는진 솔직히 잘 모르겠습니다.

저는 일단 윈도우 32 API(windows.h 헤더 파일 등등)만 잘 딸려와서 설치하면 되므로 일단 쓰레드 모델은 win32로 설치했습니다.

 

빌드 리비전 선택하라는데 rev1 밖에없네요

 

C런타임은 msvcrt로 선택했습니다. 


https://stackoverflow.com/questions/67848972/differences-between-msvcrt-ucrt-and-vcruntime-libraries

 

Differences between msvcrt, ucrt and vcruntime libraries

Can someone please explain the difference between these? Internet lookup has confused me. I am using VS2019.

stackoverflow.com

차이는 여기에 나와있네요. 솔직히 정확히 잘 이해는 되지 않는데 msvcrt 가 C99랑 호환이 안된다는 점이 좀 걸리네요. 이건 나중에 확인이 필요할듯 싶습니다. 컴파일 옵션을 줘도 안되려나..?

 

 

경로는 C에 설치하도록 하겠습니다.

이거 폴더 고르는 인터페이스가 이 프로그램에서 따로 만든거 같은데 진짜  쓰레기 같을 정도로 불편합니다. 그냥 윈도우 기본 FolderDialog 쓰지 왜 이런짓거리를 했는지 참..

 

 

설치 끝내고 mingw64 MinGW 로 폴더 이름 변경하고 시스템 환경 변수에 등록해줍니다.

 

gcc --version 으로 확인해보니 편안하게 13.1.0 버전으로 업데이트 된 걸 확인할 수 있습니다. 쓰던 컴파일러가 2017년에 멈춰있었다니.. 뭐 C언어도 C99가 최신이라고 하는 마당에 별로 중요한건 아니지만요.

 

업데이트가 끝났으면 VSCode로 돌아와 F1 또는 Ctrl + Shift + P 로 설정창을 열고 C/C++ 구성 편집 UI 모드를 들어갑니다.

 

컴파일러 경로를 C:/MinGW/bin/gcc.exe 로 변경합니다.

 

IntelliSence 모드를 windows-gcc-x64 로 변경합니다. 아까 64bit용 GCC 컴파일러를 받았기 때문에 이렇게 하면 될 듯

 

${workspaceFolder}/**
C:\MinGW\include

 

포함 경로(Include Path)를 다음과 같이 수정해야 Include 경로가 멀쩡하게 분석이 됩니다.

 

JSON으로 보면 대강 이렇게 됩니다.

저는 웬만해서 C언어 설정할땐 JSON보다 UI로 설정하는걸 선호해서 이렇게 했습니다.

C언어랑 C++ 표준은 기본으로 C17 / C++17 로 설정되네요

 

C++ 17은 써봤는데 C17은 제가 알기로 C언어 표준 중 꽤나 최신의 것일탠데 음. C99 이외의 것은 안써봐서 잘 모르겠습니다

 

windows.h 파일과 NTSYSAPI 에 Ctrl + 클릭해보니 이제 잡아놓은 MinGW 경로로 표준 라이브러리 폴더가 잘 분석되며 문제가 해결된 걸 볼 수 있습니다.

 

빨간줄 없는것도 확인했으니 이제 빌드해봐야죠..!

 

오류나 경고 하나 없이 빌드가 성공한것을 확인해볼 수 있습니다 ㅠㅠ

오늘도 성공!!

 

 

 

 

다크소울 게임 테스트

빌드한 dll 을 게임에 넣고 실행해보니 1.15 버전에서 문제 없이 60프레임 제한 해제가 된 것을 볼 수 있습니다.

그런데 이상하게도 실행 파일 버전은 60프레임 해제가 안되네요. 똑같이 작업했는데 😢

 

https://github.com/pgh268400/DS3DebugFPS_OldVersion

 

GitHub - pgh268400/DS3DebugFPS_OldVersion: ⚡ Older version of D3DDebugFPS for mods using older versions such as Cinder

⚡ Older version of D3DDebugFPS for mods using older versions such as Cinder - GitHub - pgh268400/DS3DebugFPS_OldVersion: ⚡ Older version of D3DDebugFPS for mods using older versions such as Cinder

github.com

작업한걸 Github Fork 한 저장소에 곧 올릴생각입니다

 

결론

- GCC는 제대로 된 곳에서 최신 버전 깔자. 현재 Source Forge에서 배포하는거 다 망가지고 이상함

- GCC 잘 깔았으면 VSCode에서도 경로 설정 잘 해줍시다.

 

 

참고

https://jhnyang.tistory.com/440

 

[VSCode] C/C++ 개발환경 세팅하기, 비주얼코드 c_cpp_properties.json, tasks.json 설정

안녕하세요 양햄찌 블로그 주인장입니다. 오늘은 VSCode에서 C/C++ 개발환경 세팅하는 방법에 대해 알아볼거예요. Visual Studio Code 윈도우 C/C++개발을 위한 설정 세팅 1. 필요한 익스텐션 2. c_cpp_properti

jhnyang.tistory.com

 

SNS 공유하기
💬 댓글 개
이모티콘창 닫기
울음
안녕
감사해요
당황
피폐

이모티콘을 클릭하면 댓글창에 입력됩니다.