diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 95be0843..82c9c11f 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -12,4 +12,5 @@ add_subdirectory(D2Win) add_subdirectory(D2Sound) add_subdirectory(D2MCPClient) add_subdirectory(D2Debugger) +add_subdirectory(Game) diff --git a/source/Fog/definitions/Fog.1.10f.def b/source/Fog/definitions/Fog.1.10f.def index ce0994d9..e8f82a04 100644 --- a/source/Fog/definitions/Fog.1.10f.def +++ b/source/Fog/definitions/Fog.1.10f.def @@ -30,7 +30,7 @@ EXPORTS FOG_csprintf @10018 NONAME FOG_InitErrorMgr @10019 NONAME ; FOG_10020 @10020 NONAME - FOG_10021 @10021 NONAME + FOG_SetLogPrefix @10021 NONAME FOG_GetSystemInfo @10022 NONAME FOG_DisplayAssert @10023 NONAME FOG_DisplayHalt @10024 NONAME diff --git a/source/Fog/include/Fog.h b/source/Fog/include/Fog.h index f09a0b1f..16841189 100644 --- a/source/Fog/include/Fog.h +++ b/source/Fog/include/Fog.h @@ -149,7 +149,7 @@ typedef int (*D2ExceptionCallback)(); D2FUNC_DLL(FOG, csprintf, const char*, __cdecl, (char* szDest, const char* szFormat, ...), 0xDD90) //Fog.#10018 D2FUNC_DLL(FOG, InitErrorMgr, void, __fastcall, (const char* szProgramName, D2ExceptionCallback pExceptionCallback, const char* szVersion, BOOL bLogToFile), 0xE1B0) //Fog.#10019 -D2FUNC_DLL(FOG, 10021, int, __fastcall, (const char* szLogName), 0xE1A0) //Fog.#10021 +D2FUNC_DLL(FOG, SetLogPrefix, int, __fastcall, (const char* szLogPrefix), 0xE1A0) //Fog.#10021 D2FUNC_DLL(FOG, GetSystemInfo, void*, __cdecl, (), 0xDF20) //Fog.#10022 D2FUNC_DLL(FOG, DisplayAssert, void, __cdecl, (const char* szMsg, const char* szFile, int nLine), 0xED30) //Fog.#10023 D2FUNC_DLL(FOG, DisplayHalt, void, __cdecl, (const char* szMsg, const char* szFile, int nLine), 0xED60) //Fog.#10024 @@ -169,6 +169,8 @@ D2FUNC_DLL(FOG, FreePool, void, __fastcall, (void* pMemPool, void* pFree, const D2FUNC_DLL(FOG, ReallocPool, void*, __fastcall, (void* pMemPool, void* pMemory, int nSize, const char* szFile, int nLine, int n0), 0x9060) //Fog.#10047 D2FUNC_DLL(FOG, 10050_EnterCriticalSection, void, __fastcall, (CRITICAL_SECTION* pCriticalSection, int nLine), 0xDC20) //Fog.#10050 D2FUNC_DLL(FOG, 10055_GetSyncTime, int32_t, __fastcall, (), 0xA690) //Fog.#10055 +// Noop, same as 10048, 10049, 10053, 10054, 10146, 10194, 10195, 10196, 10197, 10220, 10221, 10225, 10232, 10240, 10241, 10242 +D2FUNC_DLL(FOG, 10082_Noop, void, __fastcall, (), 0x1DE0) //Fog.#10082 D2FUNC_DLL(FOG, 10083_Cos_LUT, float, __stdcall, (int16_t index), 0x1DF0) //Fog.#10083 D2FUNC_DLL(FOG, 10084_Sin_LUT, float, __stdcall, (int16_t index), 0x1E10) //Fog.#10084 D2FUNC_DLL(FOG, Decode14BitsFromString, unsigned, __stdcall, (uint16_t* p2Characters), 0x1B20) //Fog.#10085 @@ -193,6 +195,7 @@ D2FUNC_DLL(FOG, MPQFileClose, void, __fastcall, (HSFILE pFile), 0x11610) D2FUNC_DLL(FOG, MPQFileRead, BOOL, __fastcall, (HSFILE pFile, void* pBuffer, size_t nSize, int* nBytesRead, uint32_t, uint32_t, uint32_t), 0x11620) //Fog.#10104 D2FUNC_DLL(FOG, MPQFileGetSize, size_t, __fastcall, (HSFILE pFileHandle, uint32_t* lpFileSizeHigh), 0x11650) //Fog.#10105 D2FUNC_DLL(FOG, GetSavePath, size_t, __fastcall, (char* pPathBuffer, size_t nBufferSize), 0x1900) //Fog.#10115 +D2FUNC_DLL(FOG, GetInstallPath, BOOL, __fastcall, (char* pPathBuffer, size_t nBufferSize), 0x1900) //Fog.#10116 D2FUNC_DLL(FOG, ComputeStringCRC16, uint16_t, __stdcall, (const char* szString), 0x3DB0) //Fog.#10137 D2FUNC_DLL(FOG, CreateNewPoolSystem, void, __cdecl, (void** pMemPoolSystem, const char* szName, uint32_t nPools, uint32_t nUnused), 0xA280) //Fog.#10142 D2FUNC_DLL(FOG, DestroyMemoryPoolSystem, void, __cdecl, (void* pMemoryPoolSystem), 0xA100) //Fog.#10143 diff --git a/source/Game/CMakeLists.txt b/source/Game/CMakeLists.txt new file mode 100644 index 00000000..22b2d242 --- /dev/null +++ b/source/Game/CMakeLists.txt @@ -0,0 +1,18 @@ + +add_executable(Game WIN32 + src/Main.cpp + include/Main.h + + resources/Game.rc + resources/Game.${D2MOO_ORDINALS_VERSION}.rc +) +target_include_directories(Game PRIVATE include) +target_link_libraries(Game + PRIVATE + Fog + Storm + D2Win + D2Gfx + D2Sound + D2MCPClient +) diff --git a/source/Game/include/Main.h b/source/Game/include/Main.h new file mode 100644 index 00000000..6bc9ab46 --- /dev/null +++ b/source/Game/include/Main.h @@ -0,0 +1,210 @@ +#pragma once +#include + +//TODO +#define D2_VERSION_EXPANSION true +#define D2_VERSION_MAJOR 1 +#define D2_VERSION_MINOR 10 +#define D2_HAS_OPENGL false + +#define D2_HAS_MULTILAN defined(D2_VERSION_100) // TODO: figure out when this was removed + +/* + * Project definitions + */ +#define GAMEAPI __fastcall +#define KEYHOOKAPI __stdcall +#define QUERYINTAPI __fastcall + +#define MAX_REG_KEY 1024 +#define NUM_GAME_MOD 7 + +/* + * String definitions + */ +#define GAME_FILE_INI "D2.ini" +#define GAME_OK "DIABLO_II_OK" + +#define SYS_NAME "Diablo II" +#define SYS_LOG_PREFIX "D2" + +#define SVC_NAME "DIABLO2SRV" +#define SVC_DISPLAYNAME "Diablo II Server" + +#define INIT_NAME "d2server" + +#define REG_KEY_HOME "Diablo II" +#define REG_VAL_RENDER "Render" +#define REG_PATH_HOME "SOFTWARE\\Blizzard Entertainment\\Diablo II" +#define REG_PATH_BETA "SOFTWARE\\Blizzard Entertainment\\Diablo II Beta" +#define REG_PATH_VIDEO "SOFTWARE\\Blizzard Entertainment\\Diablo II\\VideoConfig" + +#define HMOD_KEYHOOK "Keyhook.dll" +#define PROC_QUERYINT "QueryInterface" +#define PROC_KEYHOOK "InstallKeyboardHook" +#define PROC_UNKEYHOOK "UninstallKeyboardHook" + +#define ERRMSG_TITLE "Diablo 2" +#define ERRMSG_LOADMOD "Cannot load %s: Error %d" +#define ERRMSG_INSTALLSVC "Unable to install or run service. Verify that you have administrator access and that the program is located on a local drive." + +#define CMDLINE_TOKEN "-" +#define CMDLINE_SVC "SvcCmdLine" +#define CMDLINE_USE "UseCmdLine" +#define CMDLINE_BNET "-skiptobnet" +#define CMDLINE_ADD_BNET "%s -skiptobnet" +#define CMDLINE_INSTALL "-install" +#define CMDLINE_SZ "CmdLine" + +/* + * Define module types + */ +enum D2_MODULES +{ + MODULE_NONE, + MODULE_CLIENT, + MODULE_SERVER, + MODULE_MULTIPLAYER, + MODULE_LAUNCHER, +#if D2_HAS_MULTILAN + MODULE_MULTILAN, +#endif + MODULE_EXPAND, + D2_MODULES_COUNT +}; + +/* + * Define graphic render modes + */ +enum +{ + DISPLAYTYPE_NONE, + DISPLAYTYPE_GDI, + DISPLAYTYPE_UNUSED, + DISPLAYTYPE_RAVE, + DISPLAYTYPE_GLIDE, + DISPLAYTYPE_OPENGL, + DISPLAYTYPE_DIRECT3D +}; + +/* + * Define command line types + */ +enum +{ + CMD_BOOLEAN, + CMD_INTEGER, + CMD_STRING +}; + +/* + * Create structure for game configuration parameters + * 888 bytes in size + */ +#pragma pack(push, 1) +struct D2GameCfgStrc +{ +#if D2_VERSION_EXPANSION // Not present in old versions of the game such as 1.00 + BOOL bIsExpansion; +#endif + BYTE bWindow; + BYTE b3DFX; + BYTE bOpenGL; + BYTE bRave; + BYTE bD3D; + BYTE bPerspective; + BYTE bQuality; + DWORD dwGamma; + BYTE bVSync; + DWORD dwFramerate; + DWORD dwGameType; + WORD wJoinID; // Dangerous ! We may be overwriting szGameName since code expects a DWORD + char szGameName[24]; + char szServerIP[24]; + char szBattleNetIP[24]; + char szMCPIP[24]; + BYTE _0076[4]; + BYTE bNoPK; + BYTE bOpenC; + BYTE bAmazon; + BYTE bPaladin; + BYTE bSorceress; + BYTE bNecromancer; + BYTE bBarbarian; +#if D2_VERSION_EXPANSION + BYTE bDruid; + BYTE bAssassin; +#endif + BYTE bInvincible; + BYTE _0082[48]; + char szName[24]; + BYTE szRealm[256]; + BYTE _01D0[0x18]; + DWORD dwCTemp; + BYTE bNoMonsters; + DWORD dwMonsterClass; + BYTE bMonsterInfo; + DWORD dwMonsterDebug; + BYTE bRare; + BYTE bUnique; + BYTE _01DA[2]; // Possibly Set/Magic + DWORD dwAct; + BYTE bNoPreload; + BYTE bDirect; + BYTE bLowEnd; + BYTE bNoCompress; + DWORD dwArena; + BYTE _01E8[6]; // Related to Arena +#ifndef VERSION_100 // TODO: figure out when this was added. Probably in 1.10 + void* pSomeCallback; + BYTE bTxt; +#endif + BYTE bLog; + BYTE bMsgLog; + BYTE bSafeMode; + BYTE bNoSave; + DWORD dwSeed; + BYTE bCheats; +#ifndef VERSION_100 // TODO: figure out when this was added + BYTE bTeen; +#endif + BYTE bNoSound; + BYTE bQuests; + BYTE _01F9; + +#ifndef VERSION_100 // TODO: figure out when this was added. Probably in 1.10 + BYTE bBuild; +#endif + DWORD dwComInt; + BYTE _01FE[308]; // Related to ComInt + BYTE bSkipToBNet; + BYTE _0333[112]; +}; +#pragma pack(pop) + +/* + * Create structure for command line arguments + * 60 bytes in size + */ +struct D2CmdArgStrc +{ + char szSection[16]; + char szKey[16]; + char szCommand[16]; + DWORD dwType; + DWORD dwIndex; + DWORD dwDefault; +}; + +/* + * Prototypes + */ +int GAMEAPI ParseCmdLine(D2GameCfgStrc* pCfg, char *argv); +void GAMEAPI stoLower(char *s); +int GAMEAPI GetCmdIndex(const char *s); +int GAMEAPI GetCmdData(char *s); +int GAMEAPI GameStart(HINSTANCE hInstance, D2GameCfgStrc* pCfg, D2_MODULES nModType); +void GAMEAPI SaveCmdLine(const char *argv[]); +int GAMEAPI GameInit(DWORD dwNumServicesArgs, LPSTR* lpServiceArgVectors); +VOID WINAPI D2ServerServiceMain(DWORD dwArgc, LPTSTR *lpszArgv); +VOID WINAPI D2ServerServiceHandlerProc(DWORD dwCtrlCode); diff --git a/source/Game/resources/Game.1.10f.rc b/source/Game/resources/Game.1.10f.rc new file mode 100644 index 00000000..529acbe3 Binary files /dev/null and b/source/Game/resources/Game.1.10f.rc differ diff --git a/source/Game/resources/Game.rc b/source/Game/resources/Game.rc new file mode 100644 index 00000000..de7de378 Binary files /dev/null and b/source/Game/resources/Game.rc differ diff --git a/source/Game/resources/ICON/Blank.ico b/source/Game/resources/ICON/Blank.ico new file mode 100644 index 00000000..d3a9c7df Binary files /dev/null and b/source/Game/resources/ICON/Blank.ico differ diff --git a/source/Game/resources/ICON/Diablo.ico b/source/Game/resources/ICON/Diablo.ico new file mode 100644 index 00000000..dc63e8d1 Binary files /dev/null and b/source/Game/resources/ICON/Diablo.ico differ diff --git a/source/Game/resources/ICON/Lord of Destruction.ico b/source/Game/resources/ICON/Lord of Destruction.ico new file mode 100644 index 00000000..285ac4ff Binary files /dev/null and b/source/Game/resources/ICON/Lord of Destruction.ico differ diff --git a/source/Game/src/Main.cpp b/source/Game/src/Main.cpp new file mode 100644 index 00000000..6ba8c0b9 --- /dev/null +++ b/source/Game/src/Main.cpp @@ -0,0 +1,854 @@ +#include +#include +#include + +#include "Main.h" + +#include +#include +#include +#include +#include +#include +#include + +// Thanks to galaxyhaxz for providing the base to work on ! https://github.com/galaxyhaxz/d2src + +#define cmdidx(m) offsetof(D2GameCfgStrc, m) +D2CmdArgStrc gaCmdArguments[] = { + { "VIDEO", "WINDOW", "w", CMD_BOOLEAN, cmdidx(bWindow), 0 }, + { "VIDEO", "3DFX", "3dfx", CMD_BOOLEAN, cmdidx(b3DFX), 0 }, + { "VIDEO", "OPENGL", "opengl", CMD_BOOLEAN, cmdidx(bOpenGL), 0 }, + { "VIDEO", "D3D", "d3d", CMD_BOOLEAN, cmdidx(bRave), 0 }, + { "VIDEO", "RAVE", "rave", CMD_BOOLEAN, cmdidx(bD3D), 0 }, + { "VIDEO", "PERSPECTIVE", "per", CMD_BOOLEAN, cmdidx(bPerspective), 0 }, + { "VIDEO", "QUALITY", "lq", CMD_BOOLEAN, cmdidx(bQuality), 0 }, + { "VIDEO", "GAMMA", "gamma", CMD_INTEGER, cmdidx(dwGamma), 0 }, + { "VIDEO", "VSYNC", "vsync", CMD_BOOLEAN, cmdidx(bVSync), 0 }, + { "VIDEO", "FRAMERATE", "fr", CMD_INTEGER, cmdidx(dwFramerate), 0 }, + { "NETWORK", "SERVERIP", "s", CMD_STRING, cmdidx(szServerIP), 0 }, + { "NETWORK", "GAMETYPE", "gametype", CMD_INTEGER, cmdidx(dwGameType), 0 }, + { "NETWORK", "ARENA", "arena", CMD_INTEGER, cmdidx(dwArena), 0 }, + { "NETWORK", "JOINID", "joinid", CMD_INTEGER, cmdidx(wJoinID), 0 }, + { "NETWORK", "GAMENAME", "gamename", CMD_STRING, cmdidx(szGameName), 0 }, + { "NETWORK", "BATTLENETIP", "bn", CMD_STRING, cmdidx(szBattleNetIP), 0 }, + { "NETWORK", "MCPIP", "mcpip", CMD_STRING, cmdidx(szMCPIP), 0 }, + { "CHARACTER", "AMAZON", "ama", CMD_BOOLEAN, cmdidx(bAmazon), 1 }, + { "CHARACTER", "PALADIN", "pal", CMD_BOOLEAN, cmdidx(bPaladin), 0 }, + { "CHARACTER", "SORCERESS", "sor", CMD_BOOLEAN, cmdidx(bSorceress), 0 }, + { "CHARACTER", "NECROMANCER", "nec", CMD_BOOLEAN, cmdidx(bNecromancer), 0 }, + { "CHARACTER", "BARBARIAN", "bar", CMD_BOOLEAN, cmdidx(bBarbarian), 0 }, +#if D2_VERSION_EXPANSION && 0 // Not actually wired in original game, but we could do it! + { "CHARACTER", "DRUID", "dru", CMD_BOOLEAN, cmdidx(bDruid), 0 }, + { "CHARACTER", "ASSASSIN", "ass", CMD_BOOLEAN, cmdidx(bAssassin), 0 }, +#endif + { "CHARACTER", "INVINCIBLE", "i", CMD_BOOLEAN, cmdidx(bInvincible), 0 }, + { "CHARACTER", "NAME", "name", CMD_STRING, cmdidx(szName), 0 }, +#ifndef D2_VERSION_100 // TODO: figure out when this was added + { "CHARACTER", "REALM", "realm", CMD_STRING, cmdidx(szRealm), 0 }, +#endif + { "CHARACTER", "CTEMP", "ctemp", CMD_INTEGER, cmdidx(dwCTemp), 0 }, + { "MONSTER", "NOMONSTERS", "nm", CMD_BOOLEAN, cmdidx(bNoMonsters), 0 }, + { "MONSTER", "MONSTERCLASS", "m", CMD_INTEGER, cmdidx(dwMonsterClass), 0 }, + { "MONSTER", "MONSTERINFO", "minfo", CMD_BOOLEAN, cmdidx(bMonsterInfo), 0 }, + { "MONSTER", "MONSTERDEBUG", "md", CMD_INTEGER, cmdidx(dwMonsterDebug), 0 }, + { "ITEM", "RARE", "rare", CMD_BOOLEAN, cmdidx(bRare), 0 }, + { "ITEM", "UNIQUE", "unique", CMD_BOOLEAN, cmdidx(bUnique), 0 }, + { "INTERFACE", "ACT", "act", CMD_INTEGER, cmdidx(dwAct), 1 }, + { "DEBUG", "LOG", "log", CMD_BOOLEAN, cmdidx(bLog), 0 }, + { "DEBUG", "MSGLOG", "msglog", CMD_BOOLEAN, cmdidx(bMsgLog), 0 }, + { "DEBUG", "SAFEMODE", "safe", CMD_BOOLEAN, cmdidx(bSafeMode), 0 }, + { "DEBUG", "NOSAVE", "nosave", CMD_BOOLEAN, cmdidx(bNoSave), 0 }, + { "DEBUG", "SEED", "seed", CMD_INTEGER, cmdidx(dwSeed), 0 }, + { "NETWORK", "NOPK", "nopk", CMD_BOOLEAN, cmdidx(bNoPK), 0 }, + { "DEBUG", "CHEATS", "cheats", CMD_BOOLEAN, cmdidx(bCheats), 0 }, +#ifndef D2_VERSION_100 // TODO: figure out when this was added + { "DEBUG", "TEEN", "teen", CMD_BOOLEAN, cmdidx(bTeen), 0 }, +#endif + { "DEBUG", "NOSOUND", "ns", CMD_BOOLEAN, cmdidx(bNoSound), 0 }, + { "FILEIO", "NOPREDLOAD", "npl", CMD_BOOLEAN, cmdidx(bNoPreload), 0 }, + { "FILEIO", "DIRECT", "direct", CMD_BOOLEAN, cmdidx(bDirect), 0 }, + { "FILEIO", "LOWEND", "lem", CMD_BOOLEAN, cmdidx(bLowEnd), 0 }, + { "DEBUG", "QuEsTs", "questall", CMD_BOOLEAN, cmdidx(bQuests), 0 }, + { "NETWORK", "COMINT", "comint", CMD_INTEGER, cmdidx(dwComInt), 0 }, + { "NETWORK", "SKIPTOBNET", "skiptobnet", CMD_BOOLEAN, cmdidx(bSkipToBNet), 0 }, + { "NETWORK", "OPENC", "openc", CMD_BOOLEAN, cmdidx(bOpenC), 0 }, + { "FILEIO", "NOCOMPRESS", "nocompress", CMD_BOOLEAN, cmdidx(bNoCompress), 0 }, +#ifndef D2_VERSION_100 // TODO: figure out when this was added. Probably in 1.10 + { "TXT", "TXT", "txt", CMD_BOOLEAN, cmdidx(bTxt), 0 }, + { "BUILD", "BUILD", "build", CMD_BOOLEAN, cmdidx(bBuild), 0 }, +#endif +}; +#undef cmdidx + +/* + * Module names loaded by main game + */ +char *lpszD2Module[] = { + "none.dll", + "D2Client.dll", + "D2Server.dll", + "D2Multi.dll", + "D2Launch.dll", +#if D2_HAS_MULTILAN + "D2MultiLAN.dll", +#endif + "D2EClient.dll" +}; +static_assert(D2_MODULES_COUNT == ARRAY_SIZE(lpszD2Module), "Size of module types need to match enum."); + +/* + * Internal name type for each module + */ +char *lpszModuleType[] = { + "modstate0", + "client", + "server", + "multiplayer", + "launcher", +#if D2_HAS_MULTILAN + "multilan", +#endif + "expand" +}; +static_assert(D2_MODULES_COUNT == ARRAY_SIZE(lpszModuleType), "Size of module types need to match enum."); + +/* + * Structure for service status and handler + */ + //1.10f: Game.0x40FDA8 +SERVICE_STATUS gD2ServerServiceStatus = { + SERVICE_WIN32_OWN_PROCESS, + SERVICE_RUNNING, + SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN, + 0, + 0, + 0, + 0 +}; +//1.10f: Game.0x41345C +SERVICE_STATUS_HANDLE ghD2ServerServiceStatus = NULL; +//1.10f: Game.0x413464 +HINSTANCE ghCurrentProcess = NULL; +//1.10f: Game.0x413468 +BOOL gbD2ServerStopEvent = FALSE; +int gnCmdShow = 0; +//1.10f: Game.0x413470 +char szSRegReadBuf[MAX_REG_KEY]; +char lpZero = '\0'; + +//1.10f: Game.0x413444 +D2_MODULES geModState = MODULE_NONE; + +typedef D2_MODULES (QUERYINTAPI* ModuleInitPointer)(D2GameCfgStrc*); +//1.10f: Game.0x413448 +void* gpCurrentModuleInterface = nullptr; // Could be local, shareware builds use a table instead ? +//1.10f: Game.0x413450 +HMODULE ghModKeyhook = NULL; +//1.10f: Game.0x41344C +BOOL gbUseKeyhook = FALSE; + +//TODO: clean +//1.10f: Game.0x401040 +int GAMEAPI ParseCmdLine(D2GameCfgStrc* pCfg, char *argv) +{ + D2CmdArgStrc* pArg; + char *lpArgPos; + char *lpCfgPos; + char szArg[24]; + char szBuf[48]; + char szPath[_MAX_DIR]; + BOOL bSearchIndex; + int nResult; + int nIndex; + int nLen; + int nArgLen; + int i; + int j; + + memset(pCfg, 0, sizeof(*pCfg) - 2); + j = 0; + + if(GetCurrentDirectoryA(sizeof(szPath), szPath)) + { + nLen = strlen(szPath); + + for(i = 0; i < nLen; i++) + { + if(i >= sizeof(szPath)) + break; + else if(szPath[i] == '\\') + j++; + } + + if(j > 2) + j = 2; + + for(i = 0; i < nLen; i++) + { + if(!j) + break; + else if(szPath[i] == '\\') + j--; + } + + szPath[i] = '\0'; + strcat(szPath, GAME_FILE_INI); + + for (const D2CmdArgStrc& rCmdArg : gaCmdArguments) + { + switch (rCmdArg.dwType) + { + case CMD_INTEGER: + *(int*)lpCfgPos = GetPrivateProfileIntA(rCmdArg.szSection, rCmdArg.szKey, rCmdArg.dwDefault, szPath); + break; + case CMD_STRING: + GetPrivateProfileStringA(rCmdArg.szSection, rCmdArg.szKey, &lpZero, lpCfgPos, 16, szPath); + break; + case CMD_BOOLEAN: + *lpCfgPos = GetPrivateProfileIntA(rCmdArg.szSection, rCmdArg.szKey, rCmdArg.dwDefault, szPath) != 0; + break; + } + } + } + + nArgLen = strlen(argv); + nLen = nArgLen; + nResult = 0; + + if(nArgLen > 0) + { + char* lpszArgv = argv; + lpArgPos = (char *)(szBuf - argv); + + do + { + if(*lpszArgv == '-') + { + lpArgPos--; + lpszArgv++; + j = nResult + 1; + + if(j < nLen) + { + i = 0; + nIndex = -1; + nLen = strlen(lpszArgv); + + if(nLen > 0) + { + while(i++ < nLen) + { + if(!*lpszArgv || *lpszArgv == '-') + break; + + lpszArgv[(int)lpArgPos] = *lpszArgv++; + } + } + + szBuf[i] = '\0'; + szBuf[i + 1] = '\0'; + strcpy(szArg, szBuf); + nLen = strlen(szArg); + bSearchIndex = TRUE; + stoLower(szArg); + + if(nLen) + { + while(bSearchIndex && nLen) + { + szArg[nLen--] = '\0'; + nIndex = GetCmdIndex(szArg); + + if(nIndex != -1) + bSearchIndex = FALSE; + } + } + + i = 0; + nLen = strlen(szArg); + + if(szBuf) + { + do + { + szArg[i++] = szBuf[i + nLen]; + } while(szBuf[i]); + } + + szArg[i] = '\0'; + GetCmdData(szArg); + + if(nIndex != -1) + { + lpCfgPos = (char *)((int)pCfg + gaCmdArguments[nIndex].dwIndex); + + switch((char)gaCmdArguments[nIndex].dwType) + { + case CMD_INTEGER: + *(int *)lpCfgPos = atoi(szArg); + break; + case CMD_STRING: + strcpy(lpCfgPos, szArg); + break; + case CMD_BOOLEAN: + *lpCfgPos = TRUE; + break; + } + } + } + } + + nLen = nArgLen; + nResult = j++; + lpArgPos--; + lpszArgv++; + } while(j < nLen); + } + + return 0; +} + +void GAMEAPI stoLower(char *s) +{ + if(*s) + { + do + { + if(*s >= 'A' && *s <= 'Z') + *s += 32; + } while((s++)[1]); + } +} + +int GAMEAPI GetCmdIndex(const char *s) +{ + for (int nIndex = 0; nIndex < ARRAY_SIZE(gaCmdArguments); nIndex++) + { + if (0 == strcmp(gaCmdArguments[nIndex].szCommand, s)) + { + return nIndex; + } + } + return -1; +} + +//TODO: clean +int GAMEAPI GetCmdData(char *s) +{ + int nResult; + int nLen; + int nPos; + int i; + char bDelChar[4]; + char szBuf[24]; + + memcpy(szBuf, s, strlen(s) + 1); + nResult = 0; + nLen = strlen(s); + + if(nLen > 0) + { + bDelChar[0] = ' '; + bDelChar[1] = '\t'; + bDelChar[2] = '\n'; + bDelChar[3] = ':'; + + while(nResult < nLen) + { + i = 0; + + while(bDelChar[i] != s[nResult]) + { + if(i++ >= 4) + goto DOCOPY; + } + + nResult++; + } + } +DOCOPY: + nPos = 0; + + if(nResult < nLen) + { + bDelChar[0] = ' '; + bDelChar[1] = '\t'; + bDelChar[2] = '\n'; + bDelChar[3] = ':'; +RETRY: + i = 0; + + while(bDelChar[i] != szBuf[nResult]) + { + if(i++ >= 4) + { + s[nPos++] = szBuf[nResult++]; + + if(nResult < nLen) + goto RETRY; + + break; + } + } + } + + return 0; +} + +//1.10f: 0x4014D0 (Inlined) +D2_MODULES LoadCurrentlySelectedModule(D2GameCfgStrc* pCfg) +{ + if (geModState >= MODULE_NONE && geModState < D2_MODULES_COUNT) + { + if (HMODULE hModule = LoadLibraryA(lpszD2Module[geModState])) + { + if (FARPROC pQueryInterface = GetProcAddress(hModule, PROC_QUERYINT)) + { + gpCurrentModuleInterface = (void*)pQueryInterface(); + return (*(ModuleInitPointer*)gpCurrentModuleInterface)(pCfg); + } + + GetLastError(); + } + else + { + char szErrMsg[100]; + sprintf(szErrMsg, ERRMSG_LOADMOD, lpszD2Module[geModState], GetLastError()); + MessageBoxA(NULL, szErrMsg, ERRMSG_TITLE, MB_ICONERROR); + } + } + return MODULE_NONE; +} + +int GAMEAPI GameStart(HINSTANCE hInstance, D2GameCfgStrc* pCfg, D2_MODULES nModType) +{ + BOOL bSoundStarted = FALSE; + BOOL bGfxStarted = FALSE; + + FOG_MPQSetConfig(pCfg->bDirect, FALSE); + FOG_AsyncDataInitialize(TRUE); + FOG_10082_Noop(); + + if(geModState != MODULE_SERVER) + { + if(!D2WinLoadArchives() || !D2WinLoadOtherArchives(D2WinPromptPlayDisc, D2WinPromptExpansionDisc, 0, pCfg)) + { + D2WinUnloadArchives(); + return 0; + } +#if D2_VERSION_EXPANSION + pCfg->bIsExpansion = FOG_IsExpansion(); +#endif + } + + + int dwRenderMode = DISPLAYTYPE_NONE; + if (pCfg->b3DFX) dwRenderMode = DISPLAYTYPE_GLIDE; + else if (pCfg->bWindow) dwRenderMode = DISPLAYTYPE_GDI; +#if D2_HAS_OPENGL + else if (pCfg->bOpenGL) dwRenderMode = DISPLAYTYPE_OPENGL; +#endif + else if (pCfg->bD3D) dwRenderMode = DISPLAYTYPE_DIRECT3D; + else dwRenderMode = DISPLAYTYPE_RAVE; + + if(nModType != MODULE_SERVER) + { + if(!D2WinCreateWindow(hInstance, dwRenderMode, pCfg->bWindow, !pCfg->bNoCompress)) + return 0; + + if(pCfg->bPerspective && dwRenderMode >= DISPLAYTYPE_GLIDE) + D2GFX_SetPerspective(TRUE); + + if(!D2WinInitSpriteCache(pCfg->bWindow != 0, 0)) + { + WINDOW_Destroy(); + return 0; + } + + if(gbUseKeyhook) + { + ghModKeyhook = LoadLibraryA(HMOD_KEYHOOK); + } + + if(ghModKeyhook) + { + if (FARPROC pFunc = GetProcAddress(ghModKeyhook, PROC_KEYHOOK)) + { + ((void (KEYHOOKAPI*)(HWND))pFunc)(WINDOW_GetWindow()); + } + } + + bGfxStarted = TRUE; + } + + if(pCfg->bQuality) + { + D2GFX_ToggleLowQuality(); + } + + if(pCfg->dwGamma) + { + D2GFX_SetGamma(pCfg->dwGamma); + } + + if(pCfg->bVSync) + { + D2GFX_EnableVSync(); + } + + if (!pCfg->bIsExpansion) + { + SRegSaveValue("Diablo II", "Resolution", 0, 0); + } + + if(!pCfg->bNoSound && nModType != MODULE_SERVER) + { + D2Sound_OpenSoundSystem(pCfg->bIsExpansion); + bSoundStarted = TRUE; + } + + while(geModState != MODULE_NONE) + { + if(nModType == MODULE_SERVER) + { + if(bSoundStarted) + { + D2Sound_CloseSoundSystem(); + bSoundStarted = FALSE; + } + + if(bGfxStarted) + { + D2WinCloseSpriteCache(); + D2GFX_Release(); + bGfxStarted = FALSE; + } + } + + geModState = LoadCurrentlySelectedModule(pCfg); + } + + if (bSoundStarted) + { + D2Sound_CloseSoundSystem(); + } + + if(bGfxStarted) + { + D2WinCloseSpriteCache(); + D2GFX_Release(); + } + + D2WinUnloadArchives(); + + if(ghModKeyhook) + { + if (FARPROC pFunc = GetProcAddress(ghModKeyhook, PROC_UNKEYHOOK)) + { + pFunc(); + } + + FreeLibrary(ghModKeyhook); + } + + FOG_AsyncDataDestroy(); + D2MCPClientCloseMCP(); + + if(pCfg->dwComInt) + (*(void (**)(void))(pCfg->dwComInt + 12))(); + + FOG_DestroyMemoryPoolSystem(nullptr); + + return 0; +} + +//1.10f: Game.0x401870 +void GAMEAPI SaveCmdLine(const char* argv[]) +{ + char szSRegWriteBuf[MAX_REG_KEY * 2]; + + DWORD bUseCmdLine = FALSE; + + if(*argv && strlen(*argv)) + { + sprintf(szSRegWriteBuf, CMDLINE_ADD_BNET, *argv); + SRegSaveString(REG_KEY_HOME, CMDLINE_SZ, SREG_DEFAULT, szSRegWriteBuf); + } + else + { + SRegLoadValue(REG_KEY_HOME, CMDLINE_USE, SREG_DEFAULT, &bUseCmdLine); + + if(bUseCmdLine) + { + // Note: + SRegLoadString(REG_KEY_HOME, CMDLINE_SZ, SREG_DEFAULT, szSRegReadBuf, MAX_REG_KEY); + *argv = szSRegReadBuf; + } + else + { + strcpy(szSRegWriteBuf, CMDLINE_BNET); + SRegSaveString(REG_KEY_HOME, CMDLINE_SZ, SREG_DEFAULT, szSRegWriteBuf); + } + } + + bUseCmdLine = FALSE; + SRegSaveValue(REG_KEY_HOME, CMDLINE_USE, SREG_DEFAULT, NULL); + + if(gbD2ServerStopEvent) + { + SRegLoadString(REG_KEY_HOME, CMDLINE_SVC, SREG_DEFAULT, szSRegReadBuf, MAX_REG_KEY); + *argv = szSRegReadBuf; + } +} + +void MoveBetaSettingsToRelease() +{ + HKEY phkResult; + if (ERROR_SUCCESS == RegOpenKeyA(HKEY_LOCAL_MACHINE, REG_PATH_BETA, &phkResult)) + { + HKEY hKey; + RegCreateKeyA(HKEY_LOCAL_MACHINE, REG_PATH_HOME, &hKey); + + for (int i = 0; ; i++) + { + DWORD cchValue = MAX_PATH; + DWORD cbData = MAX_REG_KEY; + char szValue[MAX_PATH]; + BYTE bData[MAX_REG_KEY]; + DWORD dwRegType; + if (ERROR_SUCCESS != RegEnumValueA(phkResult, i, szValue, &cchValue, NULL, &dwRegType, bData, &cbData)) + { + break; + } + if (ERROR_SUCCESS != RegSetValueExA(hKey, szValue, 0, dwRegType, bData, cbData)) + { + break; + } + } + + RegCloseKey(hKey); + RegCloseKey(phkResult); + RegDeleteKeyA(HKEY_LOCAL_MACHINE, REG_PATH_BETA); + } + +} + +//1.10f: Game.0x401970 +int GAMEAPI GameInit(DWORD dwNumServicesArgs, LPSTR* lpServiceArgVectors) +{ + char *lpArgvTokens; + char **lpszModType; + char *lpArgvCmd; + char szRegPathVid[sizeof(REG_PATH_VIDEO)]; + D2GameCfgStrc pCfg; + + lpArgvCmd = &lpZero; + + if (dwNumServicesArgs > 1) + { + lpArgvCmd = lpServiceArgVectors[dwNumServicesArgs-1]; + } + + char szVersion[MAX_PATH]; + SStrPrintf(szVersion, MAX_PATH, "v%d.%02d", D2_VERSION_MAJOR, D2_VERSION_MINOR); + FOG_SetLogPrefix(SYS_LOG_PREFIX); + FOG_InitErrorMgr(SYS_NAME, NULL, szVersion, TRUE); + + if (HANDLE hEvent = OpenEventA(EVENT_MODIFY_STATE, TRUE, GAME_OK)) + { + SetEvent(hEvent); + } + + SaveCmdLine((const char**)&lpArgvCmd); + + MoveBetaSettingsToRelease(); + + char szPath[MAX_PATH] = {0}; + memset(szPath + 1, 0, MAX_PATH); + + FOG_GetInstallPath(szPath, MAX_PATH); + + if (gbD2ServerStopEvent) + { + SetCurrentDirectoryA(szPath); + } + + const size_t argvLen = strlen(lpArgvCmd) + 1; + char* lpArgvDupe = (char*)D2_ALLOC(argvLen); + SStrCopy(lpArgvDupe, lpArgvCmd, argvLen); + + int nChosenModule = MODULE_NONE; + + for (char* pCurrentParam = strtok(lpArgvDupe, "-"); pCurrentParam; pCurrentParam = strtok(0, "-")) + { + for (int i = 0; i < D2_MODULES_COUNT && pCurrentParam; i++) + { + if (0 == strncmp(pCurrentParam, lpszModuleType[i], strlen(lpszModuleType[i])) && i != MODULE_CLIENT) + { + nChosenModule = i; + } + } + } + + D2_FREE(lpArgvDupe); + ParseCmdLine(&pCfg, lpArgvCmd); + + if(!pCfg.b3DFX && !pCfg.bWindow && !pCfg.bOpenGL && !pCfg.bD3D) + { + memcpy(szRegPathVid, REG_PATH_VIDEO, sizeof(REG_PATH_VIDEO)); + + HKEY hKey; + if((ERROR_SUCCESS == RegOpenKeyExA(HKEY_CURRENT_USER, szRegPathVid, 0, KEY_QUERY_VALUE, &hKey)) || + (ERROR_SUCCESS == RegOpenKeyExA(HKEY_LOCAL_MACHINE, szRegPathVid, 0, KEY_QUERY_VALUE, &hKey)) ) + { + DWORD dwRegType = REG_DWORD_LITTLE_ENDIAN; + DWORD dwValue; + DWORD dwCbData = sizeof(dwValue); + if(ERROR_SUCCESS == RegQueryValueExA(hKey, REG_VAL_RENDER, NULL, &dwRegType, (LPBYTE)&dwValue, &dwCbData)) + { + switch(dwValue) + { + case 1: + pCfg.bD3D = TRUE; + break; + case 2: + pCfg.bOpenGL = TRUE; + break; + case 3: + pCfg.b3DFX = TRUE; + break; + case 4: + pCfg.bWindow = TRUE; + break; + default: // Rave + break; + } + + RegCloseKey(hKey); + } + } + } + + return GameStart(ghCurrentProcess, &pCfg, MODULE_LAUNCHER); +} + +INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, INT nCmdShow) +{ + SC_HANDLE schService; + + ghCurrentProcess = hInstance; + gnCmdShow = nCmdShow; + + const bool bHasBuildVersion = GetVersion() & 0x80000000; + + if(!bHasBuildVersion) + { + if(SC_HANDLE schSCManager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS)) + { + bool bCouldOpenService = false; + if (SC_HANDLE schService = OpenServiceA(schSCManager, SVC_NAME, SERVICE_ALL_ACCESS)) + { + bCouldOpenService = true; + CloseServiceHandle(schService); + } + + CloseServiceHandle(schSCManager); + + if(bCouldOpenService) + { + SERVICE_TABLE_ENTRYA DispatchTable[] = { + { SVC_NAME, D2ServerServiceMain }, + { NULL, NULL } + }; + + if (StartServiceCtrlDispatcherA(DispatchTable) == 0) + { + return 0; // Successfully started process + } + } + } + } + + if (strstr(lpCmdLine, CMDLINE_INSTALL) == nullptr) + { + // If we did not ask to install the service, simply run the game. + char* argv[2]; + argv[0] = INIT_NAME; + argv[1] = lpCmdLine; + + GameInit(ARRAY_SIZE(argv), argv); + } + else + { + // Install the service + SC_HANDLE schSCManager = NULL; + if (!bHasBuildVersion) + { + schSCManager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS); + } + if (!schSCManager) + { + MessageBoxA(NULL, ERRMSG_INSTALLSVC, SVC_DISPLAYNAME, MB_ICONEXCLAMATION | MB_SETFOREGROUND); + return 1; + } + else + { + SC_HANDLE schService = OpenServiceA(schSCManager, SVC_NAME, SERVICE_ALL_ACCESS); + if (schService == NULL && SRegSaveString("Diablo II", "SvcCmdLine", 4, "-service")) + { + CHAR szModuleName[MAX_PATH]; + GetModuleFileNameA(GetModuleHandleA(NULL), szModuleName, sizeof(szModuleName)); + schService = CreateServiceA( + schSCManager, + SVC_NAME, SVC_DISPLAYNAME, + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + szModuleName, + NULL, NULL, NULL, NULL, NULL); + } + if (schService != NULL) + { + CloseServiceHandle(schService); + } + + CloseServiceHandle(schSCManager); + } + } + + return 0; +} + +//1.10f: Game.0x401ED0 +VOID WINAPI D2ServerServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) +{ + gbD2ServerStopEvent = TRUE; + ghD2ServerServiceStatus = RegisterServiceCtrlHandlerA(SVC_NAME, D2ServerServiceHandlerProc); + SetServiceStatus(ghD2ServerServiceStatus, &gD2ServerServiceStatus); + GameInit(dwArgc, lpszArgv); + gD2ServerServiceStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(ghD2ServerServiceStatus, &gD2ServerServiceStatus); + gbD2ServerStopEvent = FALSE; +} + +VOID WINAPI D2ServerServiceHandlerProc(DWORD dwCtrlCode) +{ + switch(dwCtrlCode) + { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + gD2ServerServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus(ghD2ServerServiceStatus, &gD2ServerServiceStatus); + gbD2ServerStopEvent = TRUE; + return; + + case SERVICE_CONTROL_INTERROGATE: + SetServiceStatus(ghD2ServerServiceStatus, &gD2ServerServiceStatus); + return; + + default: + break; + } +} diff --git a/source/Storm/include/Storm.h b/source/Storm/include/Storm.h index 6e2299a4..7c24d2fa 100644 --- a/source/Storm/include/Storm.h +++ b/source/Storm/include/Storm.h @@ -543,14 +543,22 @@ D2FUNC_DLL_NP(STORM, SMsgPopRegisterState, BOOL, __stdcall, (int type), 0x1d440) /// Imported by ['D2Client.dll'] D2FUNC_DLL_NP(STORM, SMsgPushRegisterState, BOOL, __stdcall, (int type), 0x1d480); //Storm.#419 +enum SREG_Flags { + SREG_DEFAULT = 0x00, // Look into both HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER and under "Software\Blizzard Entertainment\" + SREG_EXCLUDE_LOCAL_MACHINE = 0x01, // excludes checking the HKEY_LOCAL_MACHINE hive + SREG_BATTLE_NET = 0x02, // Look under "Software\\Battle.net\\" instead + SREG_EXCLUDE_CURRENT_USER = 0x04, // excludes checking the HKEY_CURRENT_USER hive + SREG_ABSOLUTE = 0x10, // specifies that the key is not a relative key +}; + /// Imported by ['D2Direct3D.dll', 'D2DDraw.dll'] -D2FUNC_DLL_NP(STORM, SRegLoadData, int, __stdcall, (int, DWORD Type, int, LPBYTE lpData, int, LPDWORD lpcbData), 0x25840); //Storm.#421 +D2FUNC_DLL_NP(STORM, SRegLoadData, BOOL, __stdcall, (const char* keyname, const char* valuename, size_t nSize, LPBYTE lpData, uint32_t nFlags, LPDWORD lpcbData), 0x25840); //Storm.#421 /// Imported by ['D2Launch.dll', 'D2Direct3D.dll', 'Fog.dll', 'D2DDraw.dll', 'D2Client.dll'] -D2FUNC_DLL_NP(STORM, SRegLoadString, int, __stdcall, (DWORD Type, DWORD cbData, int, LPSTR, int), 0x25a00); //Storm.#422 +D2FUNC_DLL_NP(STORM, SRegLoadString, BOOL, __stdcall, (const char* keyname, const char* valuename, uint32_t nFlags, LPSTR pBuffer, size_t nBufferSize), 0x25a00); //Storm.#422 /// Imported by ['D2gfx.dll', 'D2Launch.dll', 'D2Direct3D.dll', 'Fog.dll', 'D2CMP.dll', 'D2Multi.dll', 'D2DDraw.dll', 'D2sound.dll', 'D2Game.dll', 'D2Client.dll'] -D2FUNC_DLL_NP(STORM, SRegLoadValue, BOOL, __stdcall, (const char *keyname, const char *valuename, int a3, DWORD *value), 0x25ac0); //Storm.#423 +D2FUNC_DLL_NP(STORM, SRegLoadValue, BOOL, __stdcall, (const char *keyname, const char *valuename, uint32_t nFlags, DWORD *value), 0x25ac0); //Storm.#423 /// Not imported by any .dll D2FUNC_DLL_NP(STORM, SRegSaveData, BOOL, __stdcall, (char *keyname, char *valuename, BYTE flags, BYTE *lpData, DWORD cbData), 0x25ba0); //Storm.#424