#pragma once #include #include #include #include #include #include "appfat.h" #include "multi.h" namespace devilution { enum game_info : uint8_t { GAMEINFO_NAME, GAMEINFO_PASSWORD, }; enum conn_type : uint8_t { SELCONN_ZT, SELCONN_TCP, SELCONN_LOOPBACK, }; struct PCXHeader { uint8_t Manufacturer; uint8_t Version; uint8_t Encoding; uint8_t BitsPerPixel; uint16_t Xmin; uint16_t Ymin; uint16_t Xmax; uint16_t Ymax; uint16_t HDpi; uint16_t VDpi; uint8_t Colormap[48]; uint8_t Reserved; uint8_t NPlanes; uint16_t BytesPerLine; uint16_t PaletteInfo; uint16_t HscreenSize; uint16_t VscreenSize; uint8_t Filler[54]; }; struct _SNETCAPS { uint32_t size; uint32_t flags; uint32_t maxmessagesize; uint32_t maxqueuesize; uint32_t maxplayers; uint32_t bytessec; uint32_t latencyms; uint32_t defaultturnssec; uint32_t defaultturnsintransit; }; struct _SNETEVENT { uint32_t eventid; uint32_t playerid; void *data; uint32_t databytes; }; // Note to self: Linker error => forgot a return value in cpp // We declare the StormLib methods we use here. // StormLib uses the Windows calling convention on Windows for these methods. #ifdef _WIN32 #define WINAPI __stdcall #else #define WINAPI #endif #ifdef __cplusplus struct CCritSect { SDL_mutex *m_critsect; CCritSect() { m_critsect = SDL_CreateMutex(); if (m_critsect == NULL) { ErrSdl(); } } ~CCritSect() { SDL_DestroyMutex(m_critsect); } void Enter() { if (SDL_LockMutex(m_critsect) < 0) { ErrSdl(); } } void Leave() { if (SDL_UnlockMutex(m_critsect) < 0) { ErrSdl(); } } }; #endif // Game states #define GAMESTATE_PRIVATE 0x01 #define GAMESTATE_FULL 0x02 #define GAMESTATE_ACTIVE 0x04 #define GAMESTATE_STARTED 0x08 #define GAMESTATE_REPLAY 0x80 #define PS_CONNECTED 0x10000 #define PS_TURN_ARRIVED 0x20000 #define PS_ACTIVE 0x40000 #define LEAVE_ENDING 0x40000004 #define LEAVE_DROP 0x40000006 #if defined(__GNUC__) || defined(__cplusplus) extern "C" { #endif bool SNetCreateGame(const char *pszGameName, const char *pszGamePassword, char *GameTemplateData, int GameTemplateSize, int *playerID); bool SNetDestroy(); /* SNetDropPlayer @ 106 * * Drops a player from the current game. * * playerid: The player ID for the player to be dropped. * flags: * * Returns true if the function was called successfully and false otherwise. */ bool SNetDropPlayer(int playerid, DWORD flags); /* SNetGetGameInfo @ 107 * * Retrieves specific game information from Storm, such as name, password, * stats, mode, game template, and players. * * type: The type of data to retrieve. See GAMEINFO_ flags. * dst: The destination buffer for the data. * length: The maximum size of the destination buffer. * * Returns true if the function was called successfully and false otherwise. */ bool SNetGetGameInfo(game_info type, void *dst, unsigned int length); /* SNetGetTurnsInTransit @ 115 * * Retrieves the number of turns (buffers) that have been queued * before sending them over the network. * * turns: A pointer to an integer that will receive the value. * * Returns true if the function was called successfully and false otherwise. */ bool SNetGetTurnsInTransit( DWORD *turns); // Network provider structures typedef struct _client_info { DWORD dwSize; // 60 char *pszName; char *pszVersion; DWORD dwProduct; DWORD dwVerbyte; DWORD dwUnk5; DWORD dwMaxPlayers; DWORD dwUnk7; DWORD dwUnk8; DWORD dwUnk9; DWORD dwUnk10; // 0xFF char *pszCdKey; char *pszCdOwner; DWORD dwIsShareware; DWORD dwLangId; } client_info; typedef struct _user_info { DWORD dwSize; // 16 char *pszPlayerName; char *pszUnknown; DWORD dwUnknown; } user_info; bool SNetJoinGame(char *gameName, char *gamePassword, int *playerid); /* SNetLeaveGame @ 119 * * Notifies Storm that the player has left the game. Storm will * notify all connected peers through the network provider. * * type: The leave type. It doesn't appear to be important, no documentation available. * * Returns true if the function was called successfully and false otherwise. */ bool SNetLeaveGame(int type); bool SNetReceiveMessage(int *senderplayerid, char **data, int *databytes); bool SNetReceiveTurns(int a1, int arraysize, char **arraydata, DWORD *arraydatabytes, DWORD *arrayplayerstatus); typedef void (*SEVTHANDLER)(struct _SNETEVENT *); /* SNetSendMessage @ 127 * * Sends a message to a player given their player ID. Network message * is sent using class 01 and is retrieved by the other client using * SNetReceiveMessage(). * * playerID: The player index of the player to receive the data. * Conversely, this field can be one of the following constants: * SNPLAYER_ALL | Sends the message to all players, including oneself. * SNPLAYER_OTHERS | Sends the message to all players, except for oneself. * data: A pointer to the data. * databytes: The amount of bytes that the data pointer contains. * * Returns true if the function was called successfully and false otherwise. */ bool SNetSendMessage(int playerID, void *data, unsigned int databytes); // Macro values to target specific players #define SNPLAYER_ALL -1 #define SNPLAYER_OTHERS -2 #define MPQ_OPEN_READ_ONLY 0x00000100 #define SFILE_OPEN_FROM_MPQ 0 #define SFILE_OPEN_LOCAL_FILE 0xFFFFFFFF /* SNetSendTurn @ 128 * * Sends a turn (data packet) to all players in the game. Network data * is sent using class 02 and is retrieved by the other client using * SNetReceiveTurns(). * * data: A pointer to the data. * databytes: The amount of bytes that the data pointer contains. * * Returns true if the function was called successfully and false otherwise. */ bool SNetSendTurn(char *data, unsigned int databytes); bool SFileOpenFile(const char *filename, HANDLE *phFile); // Functions implemented in StormLib #if defined(_WIN64) || defined(_WIN32) bool WINAPI SFileOpenArchive(const wchar_t *szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE *phMpq); #else bool WINAPI SFileOpenArchive(const char *szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE *phMpq); #endif bool WINAPI SFileCloseArchive(HANDLE hArchive); bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char *szFileName, DWORD dwSearchScope, HANDLE *phFile); bool WINAPI SFileReadFile(HANDLE hFile, void *buffer, DWORD nNumberOfBytesToRead, DWORD *read, int *lpDistanceToMoveHigh); DWORD WINAPI SFileGetFileSize(HANDLE hFile, uint32_t *lpFileSizeHigh = nullptr); DWORD WINAPI SFileSetFilePointer(HANDLE, int, int *, int); bool WINAPI SFileCloseFile(HANDLE hFile); bool getIniBool(const char *sectionName, const char *keyName, bool defaultValue = false); float getIniFloat(const char *sectionName, const char *keyName, float defaultValue); bool getIniValue(const char *sectionName, const char *keyName, char *string, int stringSize, const char *defaultString = ""); void setIniValue(const char *sectionName, const char *keyName, const char *value, int len = 0); void SaveIni(); int getIniInt(const char *keyname, const char *valuename, int defaultValue); void setIniInt(const char *keyname, const char *valuename, int value); void setIniFloat(const char *keyname, const char *valuename, float value); // These error codes are used and returned by StormLib. // See StormLib/src/StormPort.h #if defined(_WIN32) // https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- #define STORM_ERROR_FILE_NOT_FOUND 2 #define STORM_ERROR_HANDLE_EOF 38 #else // !defined(_WIN32) #define STORM_ERROR_FILE_NOT_FOUND ENOENT #define STORM_ERROR_HANDLE_EOF 1002 #endif /* SErrGetLastError @ 463 * * Retrieves the last error that was specifically * set for the Storm library. * * Returns the last error set within the Storm library. */ DWORD SErrGetLastError(); /* SErrSetLastError @ 465 * * Sets the last error for the Storm library and the Kernel32 library. * * dwErrCode: The error code that will be set. */ void SErrSetLastError(DWORD dwErrCode); // Values for dwErrCode #define STORM_ERROR_GAME_TERMINATED 0x85100069 #define STORM_ERROR_INVALID_PLAYER 0x8510006a #define STORM_ERROR_NO_MESSAGES_WAITING 0x8510006b #define STORM_ERROR_NOT_IN_GAME 0x85100070 /* SStrCopy @ 501 * * Copies a string from src to dest (including NULL terminator) * until the max_length is reached. * * dest: The destination array. * src: The source array. * max_length: The maximum length of dest. * */ void SStrCopy(char *dest, const char *src, int max_length); bool SFileSetBasePath(const char *); bool SNetGetOwnerTurnsWaiting(DWORD *); bool SNetUnregisterEventHandler(event_type, SEVTHANDLER); bool SNetRegisterEventHandler(event_type, SEVTHANDLER); bool SNetSetBasePlayer(int); bool SNetInitializeProvider(uint32_t provider, struct GameData *gameData); int SNetGetProviderCaps(struct _SNETCAPS *); bool SFileEnableDirectAccess(bool enable); #if defined(__GNUC__) || defined(__cplusplus) } // Additions to Storm API: #if defined(_WIN64) || defined(_WIN32) // On Windows, handles wchar conversion and calls the wchar version of SFileOpenArchive. bool SFileOpenArchive(const char *szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE *phMpq); #endif // Locks ReadFile and CloseFile under a mutex. // See https://github.com/ladislav-zezula/StormLib/issues/175 bool SFileReadFileThreadSafe(HANDLE hFile, void *buffer, DWORD nNumberOfBytesToRead, DWORD *read = nullptr, int *lpDistanceToMoveHigh = nullptr); bool SFileCloseFileThreadSafe(HANDLE hFile); // Sets the file's 64-bit seek position. inline std::uint64_t SFileSetFilePointer(HANDLE hFile, std::int64_t offset, int whence) { int high = static_cast(offset) >> 32; int low = static_cast(offset); low = SFileSetFilePointer(hFile, low, &high, whence); return (static_cast(high) << 32) | low; } // Returns the current 64-bit file seek position. inline std::uint64_t SFileGetFilePointer(HANDLE hFile) { // We use `SFileSetFilePointer` with offset 0 to get the current position // because there is no `SFileGetFilePointer`. return SFileSetFilePointer(hFile, 0, DVL_FILE_CURRENT); } #endif } // namespace devilution