├── .gitignore ├── LICENSE ├── README.md ├── gen.go ├── go.mod ├── go.sum ├── libsteam_api.dylib ├── libsteam_api.so ├── libsteam_api64.so ├── steamworks.go ├── steamworks_darwin.go ├── steamworks_linux_386.go ├── steamworks_linux_amd64.go ├── steamworks_unix.go └── steamworks_windows.go /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | steamworks_sdk_*.zip 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-steamworks 2 | 3 | ** This project is still work in progress! ** 4 | 5 | A Steamworks SDK binding for Go 6 | 7 | ## Steamworks SDK version 8 | 9 | 161 10 | 11 | ## How to use 12 | 13 | On Windows, copy one of these files on the working directory: 14 | 15 | * `steam_api.dll` (For 32bit. Copy `redistribution_bin\steam_api.dll` in the SDK) 16 | * `steam_api64.dll` (For 64bit. Copy `redistribution_bin\win64\steam_api64.dll` in the SDK) 17 | 18 | ```go 19 | package steamapi 20 | 21 | import ( 22 | "os" 23 | 24 | "github.com/hajimehoshi/go-steamworks" 25 | "golang.org/x/text/language" 26 | ) 27 | 28 | const appID = 480 // Rewrite this 29 | 30 | func init() { 31 | if steamworks.RestartAppIfNecessary(appID) { 32 | os.Exit(1) 33 | } 34 | if !steamworks.Init() { 35 | panic("steamworks.Init failed") 36 | } 37 | } 38 | 39 | func SystemLang() language.Tag { 40 | switch steamworks.SteamApps().GetCurrentGameLanguage() { 41 | case "english": 42 | return language.English 43 | case "japanese": 44 | return language.Japanese 45 | } 46 | return language.Und 47 | } 48 | ``` 49 | 50 | ## License 51 | 52 | All the source code files are licensed under Apache License 2.0. 53 | 54 | These binary files are copied from Steamworks SDK's `redistribution_bin` directory. You must follow [Valve Corporation Steamworks SDK Access Agreement](https://partner.steamgames.com/documentation/sdk_access_agreement) for these files: 55 | 56 | * `libsteam_api.dylib` (copied from `redistribution_bin/osx/libsteam_api.dylib`) 57 | * `libsteam_api.so` (copied from `redistribution_bin/linux32/libsteam_api.so`) 58 | * `libsteam_api64.so` (copied from `redistribution_bin/linux64/libsteam_api.so`) 59 | 60 | ## Resources 61 | 62 | * [Steamworks SDK](https://partner.steamgames.com/doc/sdk) 63 | -------------------------------------------------------------------------------- /gen.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // SPDX-FileCopyrightText: 2021 The go-steamworks Authors 3 | 4 | //go:build ignore 5 | 6 | package main 7 | 8 | import ( 9 | "archive/zip" 10 | "fmt" 11 | "io" 12 | "os" 13 | ) 14 | 15 | const version = "161" 16 | 17 | func main() { 18 | if err := run(); err != nil { 19 | panic(err) 20 | } 21 | } 22 | 23 | func run() error { 24 | dir, err := os.MkdirTemp("", "go-steamworks") 25 | if err != nil { 26 | return err 27 | } 28 | defer os.RemoveAll(dir) 29 | 30 | if err := processZip(dir); err != nil { 31 | return err 32 | } 33 | 34 | return nil 35 | } 36 | 37 | func processZip(dir string) error { 38 | zipfile, err := os.Open(fmt.Sprintf("steamworks_sdk_%s.zip", version)) 39 | if err != nil { 40 | if os.IsNotExist(err) { 41 | const sdkURL = "https://partner.steamgames.com/downloads/steamworks_sdk_" + version + ".zip" 42 | return fmt.Errorf("steamworks_sdk.zip must exist; download it from %s with your Steamworks account", sdkURL) 43 | } 44 | return err 45 | } 46 | defer zipfile.Close() 47 | 48 | stat, err := zipfile.Stat() 49 | if err != nil { 50 | return err 51 | } 52 | r, err := zip.NewReader(zipfile, stat.Size()) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | for path, filename := range map[string]string{ 58 | "sdk/redistributable_bin/linux32/libsteam_api.so": "libsteam_api.so", 59 | "sdk/redistributable_bin/linux64/libsteam_api.so": "libsteam_api64.so", 60 | "sdk/redistributable_bin/osx/libsteam_api.dylib": "libsteam_api.dylib", 61 | 62 | // Do not include the Windows DLLs so far. 63 | // Unlike macOS and Linux, this library is not self-contained for Windows and requries Windows DLLs for each game. 64 | // "sdk/redistributable_bin/steam_api.dll": "steam_api.dll", 65 | // "sdk/redistributable_bin/win64/steam_api64.dll": "steam_api64.dll", 66 | } { 67 | f, err := r.Open(path) 68 | if err != nil { 69 | return err 70 | } 71 | defer f.Close() 72 | 73 | var out io.Writer 74 | out, err = os.Create(filename) 75 | if err != nil { 76 | return err 77 | } 78 | 79 | if _, err := io.Copy(out, f); err != nil { 80 | return err 81 | } 82 | } 83 | 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/hajimehoshi/go-steamworks 2 | 3 | go 1.17 4 | 5 | require golang.org/x/sys v0.27.0 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= 2 | golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 3 | -------------------------------------------------------------------------------- /libsteam_api.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hajimehoshi/go-steamworks/96b2a6baef69977a98e7888777c5219bcdf394df/libsteam_api.dylib -------------------------------------------------------------------------------- /libsteam_api.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hajimehoshi/go-steamworks/96b2a6baef69977a98e7888777c5219bcdf394df/libsteam_api.so -------------------------------------------------------------------------------- /libsteam_api64.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hajimehoshi/go-steamworks/96b2a6baef69977a98e7888777c5219bcdf394df/libsteam_api64.so -------------------------------------------------------------------------------- /steamworks.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // SPDX-FileCopyrightText: 2021 The go-steamworks Authors 3 | 4 | //go:generate go run gen.go 5 | 6 | package steamworks 7 | 8 | type AppId_t uint32 9 | type CSteamID uint64 10 | type InputHandle_t uint64 11 | 12 | type ESteamAPIInitResult int32 13 | 14 | const ( 15 | ESteamAPIInitResult_OK ESteamAPIInitResult = 0 16 | ESteamAPIInitResult_FailedGeneric ESteamAPIInitResult = 1 17 | ESteamAPIInitResult_NoSteamClient ESteamAPIInitResult = 2 18 | ESteamAPIInitResult_VersionMismatch ESteamAPIInitResult = 3 19 | ) 20 | 21 | type ESteamInputType int32 22 | 23 | const ( 24 | ESteamInputType_Unknown ESteamInputType = 0 25 | ESteamInputType_SteamController ESteamInputType = 1 26 | ESteamInputType_XBox360Controller ESteamInputType = 2 27 | ESteamInputType_XBoxOneController ESteamInputType = 3 28 | ESteamInputType_GenericXInput ESteamInputType = 4 29 | ESteamInputType_PS4Controller ESteamInputType = 5 30 | ESteamInputType_AppleMFiController ESteamInputType = 6 // Unused 31 | ESteamInputType_AndroidController ESteamInputType = 7 // Unused 32 | ESteamInputType_SwitchJoyConPair ESteamInputType = 8 // Unused 33 | ESteamInputType_SwitchJoyConSingle ESteamInputType = 9 // Unused 34 | ESteamInputType_SwitchProController ESteamInputType = 10 35 | ESteamInputType_MobileTouch ESteamInputType = 11 36 | ESteamInputType_PS3Controller ESteamInputType = 12 37 | ESteamInputType_PS5Controller ESteamInputType = 13 38 | ESteamInputType_SteamDeckController ESteamInputType = 14 39 | ESteamInputType_Count ESteamInputType = 15 40 | ESteamInputType_MaximumPossibleValue ESteamInputType = 255 41 | ) 42 | 43 | const ( 44 | _STEAM_INPUT_MAX_COUNT = 16 45 | ) 46 | 47 | type EFloatingGamepadTextInputMode int32 48 | 49 | const ( 50 | EFloatingGamepadTextInputMode_ModeSingleLine EFloatingGamepadTextInputMode = 0 51 | EFloatingGamepadTextInputMode_ModeMultipleLines EFloatingGamepadTextInputMode = 1 52 | EFloatingGamepadTextInputMode_ModeEmail EFloatingGamepadTextInputMode = 2 53 | EFloatingGamepadTextInputMode_ModeNumeric EFloatingGamepadTextInputMode = 3 54 | ) 55 | 56 | type ISteamApps interface { 57 | BGetDLCDataByIndex(iDLC int) (appID AppId_t, available bool, pchName string, success bool) 58 | BIsDlcInstalled(appID AppId_t) bool 59 | GetAppInstallDir(appID AppId_t) string 60 | GetCurrentGameLanguage() string 61 | GetDLCCount() int32 62 | } 63 | 64 | type ISteamInput interface { 65 | GetConnectedControllers() []InputHandle_t 66 | GetInputTypeForHandle(inputHandle InputHandle_t) ESteamInputType 67 | Init(bExplicitlyCallRunFrame bool) bool 68 | RunFrame() 69 | } 70 | 71 | type ISteamRemoteStorage interface { 72 | FileWrite(file string, data []byte) bool 73 | FileRead(file string, data []byte) int32 74 | FileDelete(file string) bool 75 | GetFileSize(file string) int32 76 | } 77 | 78 | type ISteamUser interface { 79 | GetSteamID() CSteamID 80 | } 81 | 82 | type ISteamUserStats interface { 83 | GetAchievement(name string) (achieved, success bool) 84 | SetAchievement(name string) bool 85 | ClearAchievement(name string) bool 86 | StoreStats() bool 87 | } 88 | 89 | type ISteamUtils interface { 90 | IsSteamRunningOnSteamDeck() bool 91 | ShowFloatingGamepadTextInput(keyboardMode EFloatingGamepadTextInputMode, textFieldXPosition, textFieldYPosition, textFieldWidth, textFieldHeight int32) bool 92 | } 93 | 94 | type ISteamFriends interface { 95 | GetPersonaName() string 96 | SetRichPresence(string, string) bool 97 | } 98 | 99 | const ( 100 | flatAPI_RestartAppIfNecessary = "SteamAPI_RestartAppIfNecessary" 101 | flatAPI_InitFlat = "SteamAPI_InitFlat" 102 | flatAPI_RunCallbacks = "SteamAPI_RunCallbacks" 103 | 104 | flatAPI_SteamApps = "SteamAPI_SteamApps_v008" 105 | flatAPI_ISteamApps_BGetDLCDataByIndex = "SteamAPI_ISteamApps_BGetDLCDataByIndex" 106 | flatAPI_ISteamApps_BIsDlcInstalled = "SteamAPI_ISteamApps_BIsDlcInstalled" 107 | flatAPI_ISteamApps_GetAppInstallDir = "SteamAPI_ISteamApps_GetAppInstallDir" 108 | flatAPI_ISteamApps_GetCurrentGameLanguage = "SteamAPI_ISteamApps_GetCurrentGameLanguage" 109 | flatAPI_ISteamApps_GetDLCCount = "SteamAPI_ISteamApps_GetDLCCount" 110 | 111 | flagAPI_SteamFriends = "SteamAPI_SteamFriends_v017" 112 | flatAPI_ISteamFriends_GetPersonaName = "SteamAPI_ISteamFriends_GetPersonaName" 113 | flatAPI_ISteamFriends_SetRichPresence = "SteamAPI_ISteamFriends_SetRichPresence" 114 | 115 | flatAPI_SteamInput = "SteamAPI_SteamInput_v006" 116 | flatAPI_ISteamInput_GetConnectedControllers = "SteamAPI_ISteamInput_GetConnectedControllers" 117 | flatAPI_ISteamInput_GetInputTypeForHandle = "SteamAPI_ISteamInput_GetInputTypeForHandle" 118 | flatAPI_ISteamInput_Init = "SteamAPI_ISteamInput_Init" 119 | flatAPI_ISteamInput_RunFrame = "SteamAPI_ISteamInput_RunFrame" 120 | 121 | flatAPI_SteamRemoteStorage = "SteamAPI_SteamRemoteStorage_v016" 122 | flatAPI_ISteamRemoteStorage_FileWrite = "SteamAPI_ISteamRemoteStorage_FileWrite" 123 | flatAPI_ISteamRemoteStorage_FileRead = "SteamAPI_ISteamRemoteStorage_FileRead" 124 | flatAPI_ISteamRemoteStorage_FileDelete = "SteamAPI_ISteamRemoteStorage_FileDelete" 125 | flatAPI_ISteamRemoteStorage_GetFileSize = "SteamAPI_ISteamRemoteStorage_GetFileSize" 126 | 127 | flatAPI_SteamUser = "SteamAPI_SteamUser_v023" 128 | flatAPI_ISteamUser_GetSteamID = "SteamAPI_ISteamUser_GetSteamID" 129 | 130 | flatAPI_SteamUserStats = "SteamAPI_SteamUserStats_v013" 131 | flatAPI_ISteamUserStats_GetAchievement = "SteamAPI_ISteamUserStats_GetAchievement" 132 | flatAPI_ISteamUserStats_SetAchievement = "SteamAPI_ISteamUserStats_SetAchievement" 133 | flatAPI_ISteamUserStats_ClearAchievement = "SteamAPI_ISteamUserStats_ClearAchievement" 134 | flatAPI_ISteamUserStats_StoreStats = "SteamAPI_ISteamUserStats_StoreStats" 135 | 136 | flatAPI_SteamUtils = "SteamAPI_SteamUtils_v010" 137 | flatAPI_ISteamUtils_IsSteamRunningOnSteamDeck = "SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck" 138 | flatAPI_ISteamUtils_ShowFloatingGamepadTextInput = "SteamAPI_ISteamUtils_ShowFloatingGamepadTextInput" 139 | ) 140 | 141 | type steamErrMsg [1024]byte 142 | 143 | func (s *steamErrMsg) String() string { 144 | for i, b := range s { 145 | if b == 0 { 146 | return string(s[:i]) 147 | } 148 | } 149 | return "" 150 | } 151 | -------------------------------------------------------------------------------- /steamworks_darwin.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // SPDX-FileCopyrightText: 2021 The go-steamworks Authors 3 | 4 | package steamworks 5 | 6 | import ( 7 | _ "embed" 8 | ) 9 | 10 | //go:embed libsteam_api.dylib 11 | var libSteamAPI []byte 12 | -------------------------------------------------------------------------------- /steamworks_linux_386.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // SPDX-FileCopyrightText: 2021 The go-steamworks Authors 3 | 4 | package steamworks 5 | 6 | import ( 7 | _ "embed" 8 | ) 9 | 10 | //go:embed libsteam_api.so 11 | var libSteamAPI []byte 12 | -------------------------------------------------------------------------------- /steamworks_linux_amd64.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // SPDX-FileCopyrightText: 2021 The go-steamworks Authors 3 | 4 | package steamworks 5 | 6 | import ( 7 | _ "embed" 8 | ) 9 | 10 | //go:embed libsteam_api64.so 11 | var libSteamAPI []byte 12 | -------------------------------------------------------------------------------- /steamworks_unix.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // SPDX-FileCopyrightText: 2021 The go-steamworks Authors 3 | 4 | //go:build !windows 5 | 6 | package steamworks 7 | 8 | import ( 9 | _ "embed" 10 | "fmt" 11 | "os" 12 | "path/filepath" 13 | "runtime" 14 | "unsafe" 15 | ) 16 | 17 | // #cgo LDFLAGS: -ldl 18 | // 19 | // #include 20 | // #include 21 | // #include 22 | // #include 23 | // 24 | // static uintptr_t dlsym_(uintptr_t handle, const char* name) { 25 | // return (uintptr_t)dlsym((void*)handle, name); 26 | // } 27 | // 28 | // static const char* uintptrToChar(uintptr_t str) { 29 | // return (const char*)str; 30 | // } 31 | // 32 | // static uint8_t callFunc_Bool(uintptr_t f) { 33 | // return ((bool (*)())(f))(); 34 | // } 35 | // 36 | // static uint8_t callFunc_Bool_Ptr(uintptr_t f, uintptr_t arg0) { 37 | // return ((bool (*)(void*))(f))((void*)arg0); 38 | // } 39 | // 40 | // static uint8_t callFunc_Bool_Ptr_Bool(uintptr_t f, uintptr_t arg0, uint8_t arg1) { 41 | // return ((bool (*)(void*, bool))(f))((void*)arg0, (bool)arg1); 42 | // } 43 | // 44 | // static uint8_t callFunc_Bool_Ptr_Int32(uintptr_t f, uintptr_t arg0, int32_t arg1) { 45 | // return ((bool (*)(void*, int32_t))(f))((void*)arg0, arg1); 46 | // } 47 | // 48 | // static uint8_t callFunc_Bool_Ptr_Int32_Int32_Int32_Int32_Int32(uintptr_t f, uintptr_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5) { 49 | // return ((bool (*)(void*, int32_t, int32_t, int32_t, int32_t, int32_t))(f))((void*)arg0, arg1, arg2, arg3, arg4, arg5); 50 | // } 51 | // 52 | // static uint8_t callFunc_Bool_Ptr_Ptr(uintptr_t f, uintptr_t arg0, uintptr_t arg1) { 53 | // return ((bool (*)(void*, void*))(f))((void*)arg0, (void*)arg1); 54 | // } 55 | // 56 | // static uint8_t callFunc_Bool_Ptr_Ptr_Ptr(uintptr_t f, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2) { 57 | // return ((bool (*)(void*, void*, void*))(f))((void*)arg0, (void*)arg1, (void*)arg2); 58 | // } 59 | // 60 | // static uint8_t callFunc_Bool_Ptr_Ptr_Ptr_Int32(uintptr_t f, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, int32_t arg3) { 61 | // return ((bool (*)(void*, void*, void*, int32_t))(f))((void*)arg0, (void*)arg1, (void*)arg2, arg3); 62 | // } 63 | // 64 | // static uint8_t callFunc_Bool_Int32(uintptr_t f, uint32_t arg0) { 65 | // return ((bool (*)(uint32_t))(f))(arg0); 66 | // } 67 | // 68 | // static int32_t callFunc_Int32_Ptr(uintptr_t f, uintptr_t arg0) { 69 | // return ((int32_t (*)(void*))(f))((void*)arg0); 70 | // } 71 | // 72 | // static int32_t callFunc_Int32_Ptr_Int32_Ptr_Int32(uintptr_t f, uintptr_t arg0, int32_t arg1, uintptr_t arg2, int32_t arg3) { 73 | // return ((int32_t (*)(void*, int32_t, void*, int32_t))(f))((void*)arg0, arg1, (void*)arg2, arg3); 74 | // } 75 | // 76 | // static int32_t callFunc_Int32_Ptr_Int32_Ptr_Ptr_Ptr_Int32(uintptr_t f, uintptr_t arg0, int32_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, int32_t arg5) { 77 | // return ((int32_t (*)(void*, int32_t, void*, void*, void*, int32_t))(f))((void*)arg0, arg1, (void*)arg2, (void*)arg3, (void*)arg4, arg5); 78 | // } 79 | // 80 | // static int32_t callFunc_Int32_Ptr_Int64(uintptr_t f, uintptr_t arg0, int64_t arg1) { 81 | // return ((int32_t (*)(void*, int64_t))(f))((void*)arg0, arg1); 82 | // } 83 | // 84 | // static int32_t callFunc_Int32_Ptr_Ptr(uintptr_t f, uintptr_t arg0, uintptr_t arg1) { 85 | // return ((int32_t (*)(void*, void*))(f))((void*)arg0, (void*)arg1); 86 | // } 87 | // 88 | // static int32_t callFunc_Int32_Ptr_Ptr_Ptr_Int32(uintptr_t f, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, int32_t arg3) { 89 | // return ((int32_t (*)(void*, void*, void*, int32_t))(f))((void*)arg0, (void*)arg1, (void*)arg2, arg3); 90 | // } 91 | // 92 | // static int64_t callFunc_Int64_Ptr(uintptr_t f, uintptr_t arg0) { 93 | // return ((int64_t (*)(void*))(f))((void*)arg0); 94 | // } 95 | // 96 | // static uintptr_t callFunc_Ptr(uintptr_t f) { 97 | // return (uintptr_t)((void* (*)())(f))(); 98 | // } 99 | // 100 | // static uintptr_t callFunc_Ptr_Ptr(uintptr_t f, uintptr_t arg0) { 101 | // return (uintptr_t)((void* (*)(void*))(f))((void*)arg0); 102 | // } 103 | // 104 | // static void callFunc_Void(uintptr_t f) { 105 | // ((void (*)())(f))(); 106 | // } 107 | // 108 | // static void callFunc_Void_Ptr_Bool(uintptr_t f, uintptr_t arg0, uint8_t arg1) { 109 | // ((void (*)(void*, bool))(f))((void*)arg0, (bool)arg1); 110 | // } 111 | import "C" 112 | 113 | type lib struct { 114 | lib C.uintptr_t 115 | procs map[string]C.uintptr_t 116 | } 117 | 118 | type funcType int 119 | 120 | const ( 121 | funcType_Bool funcType = iota 122 | funcType_Bool_Ptr 123 | funcType_Bool_Ptr_Bool 124 | funcType_Bool_Ptr_Int32 125 | funcType_Bool_Ptr_Int32_Int32_Int32_Int32_Int32 126 | funcType_Bool_Ptr_Ptr 127 | funcType_Bool_Ptr_Ptr_Ptr 128 | funcType_Bool_Ptr_Ptr_Ptr_Int32 129 | funcType_Bool_Int32 130 | funcType_Int32_Int64 131 | funcType_Int32_Ptr 132 | funcType_Int32_Ptr_Int32_Ptr_Int32 133 | funcType_Int32_Ptr_Int32_Ptr_Ptr_Ptr_Int32 134 | funcType_Int32_Ptr_Int64 135 | funcType_Int32_Ptr_Ptr 136 | funcType_Int32_Ptr_Ptr_Ptr_Int32 137 | funcType_Int64_Ptr 138 | funcType_Ptr 139 | funcType_Ptr_Ptr 140 | funcType_Void 141 | funcType_Void_Ptr_Bool 142 | ) 143 | 144 | func (l *lib) call(ftype funcType, name string, args ...uintptr) (C.uint64_t, error) { 145 | if l.procs == nil { 146 | l.procs = map[string]C.uintptr_t{} 147 | } 148 | 149 | if _, ok := l.procs[name]; !ok { 150 | cname := C.CString(name) 151 | defer C.free(unsafe.Pointer(cname)) 152 | l.procs[name] = C.dlsym_(l.lib, cname) 153 | } 154 | 155 | f := l.procs[name] 156 | switch ftype { 157 | case funcType_Bool: 158 | return C.uint64_t(C.callFunc_Bool(f)), nil 159 | case funcType_Bool_Ptr: 160 | return C.uint64_t(C.callFunc_Bool_Ptr(f, C.uintptr_t(args[0]))), nil 161 | case funcType_Bool_Ptr_Bool: 162 | return C.uint64_t(C.callFunc_Bool_Ptr_Bool(f, C.uintptr_t(args[0]), C.uint8_t(args[1]))), nil 163 | case funcType_Bool_Ptr_Int32: 164 | return C.uint64_t(C.callFunc_Bool_Ptr_Int32(f, C.uintptr_t(args[0]), C.int32_t(args[1]))), nil 165 | case funcType_Bool_Ptr_Int32_Int32_Int32_Int32_Int32: 166 | return C.uint64_t(C.callFunc_Bool_Ptr_Int32_Int32_Int32_Int32_Int32(f, C.uintptr_t(args[0]), C.int32_t(args[1]), C.int32_t(args[2]), C.int32_t(args[3]), C.int32_t(args[4]), C.int32_t(args[5]))), nil 167 | case funcType_Bool_Ptr_Ptr: 168 | return C.uint64_t(C.callFunc_Bool_Ptr_Ptr(f, C.uintptr_t(args[0]), C.uintptr_t(args[1]))), nil 169 | case funcType_Bool_Ptr_Ptr_Ptr: 170 | return C.uint64_t(C.callFunc_Bool_Ptr_Ptr_Ptr(f, C.uintptr_t(args[0]), C.uintptr_t(args[1]), C.uintptr_t(args[2]))), nil 171 | case funcType_Bool_Ptr_Ptr_Ptr_Int32: 172 | return C.uint64_t(C.callFunc_Bool_Ptr_Ptr_Ptr_Int32(f, C.uintptr_t(args[0]), C.uintptr_t(args[1]), C.uintptr_t(args[2]), C.int32_t(args[3]))), nil 173 | case funcType_Bool_Int32: 174 | return C.uint64_t(C.callFunc_Bool_Int32(f, C.uint32_t(args[0]))), nil 175 | case funcType_Int32_Ptr: 176 | return C.uint64_t(C.callFunc_Int32_Ptr(f, C.uintptr_t(args[0]))), nil 177 | case funcType_Int32_Ptr_Int32_Ptr_Int32: 178 | return C.uint64_t(C.callFunc_Int32_Ptr_Int32_Ptr_Int32(f, C.uintptr_t(args[0]), C.int32_t(args[1]), C.uintptr_t(args[2]), C.int32_t(args[3]))), nil 179 | case funcType_Int32_Ptr_Int32_Ptr_Ptr_Ptr_Int32: 180 | return C.uint64_t(C.callFunc_Int32_Ptr_Int32_Ptr_Ptr_Ptr_Int32(f, C.uintptr_t(args[0]), C.int32_t(args[1]), C.uintptr_t(args[2]), C.uintptr_t(args[3]), C.uintptr_t(args[4]), C.int32_t(args[5]))), nil 181 | case funcType_Int32_Ptr_Int64: 182 | return C.uint64_t(C.callFunc_Int32_Ptr_Int64(f, C.uintptr_t(args[0]), C.int64_t(args[1]))), nil 183 | case funcType_Int32_Ptr_Ptr: 184 | return C.uint64_t(C.callFunc_Int32_Ptr_Ptr(f, C.uintptr_t(args[0]), C.uintptr_t(args[1]))), nil 185 | case funcType_Int32_Ptr_Ptr_Ptr_Int32: 186 | return C.uint64_t(C.callFunc_Int32_Ptr_Ptr_Ptr_Int32(f, C.uintptr_t(args[0]), C.uintptr_t(args[1]), C.uintptr_t(args[2]), C.int32_t(args[3]))), nil 187 | case funcType_Int64_Ptr: 188 | return C.uint64_t(C.callFunc_Int64_Ptr(f, C.uintptr_t(args[0]))), nil 189 | case funcType_Ptr: 190 | return C.uint64_t(C.callFunc_Ptr(f)), nil 191 | case funcType_Ptr_Ptr: 192 | return C.uint64_t(C.callFunc_Ptr_Ptr(f, C.uintptr_t(args[0]))), nil 193 | case funcType_Void: 194 | C.callFunc_Void(f) 195 | return 0, nil 196 | case funcType_Void_Ptr_Bool: 197 | C.callFunc_Void_Ptr_Bool(f, C.uintptr_t(args[0]), C.uint8_t(args[1])) 198 | return 0, nil 199 | } 200 | 201 | return 0, fmt.Errorf("steamworks: function %s not implemented", name) 202 | } 203 | 204 | func loadLib() (C.uintptr_t, error) { 205 | dir, err := os.MkdirTemp("", "") 206 | if err != nil { 207 | return 0, err 208 | } 209 | 210 | ext := ".so" 211 | if runtime.GOOS == "darwin" { 212 | ext = ".dylib" 213 | } 214 | path := filepath.Join(dir, "libsteam_api"+ext) 215 | if err := os.WriteFile(path, libSteamAPI, 0644); err != nil { 216 | return 0, err 217 | } 218 | 219 | cpath := C.CString(path) 220 | defer C.free(unsafe.Pointer(cpath)) 221 | 222 | lib := C.uintptr_t(uintptr(C.dlopen(cpath, C.RTLD_LAZY))) 223 | if lib == 0 { 224 | return 0, fmt.Errorf("steamworks: dlopen failed: %s", C.GoString(C.dlerror())) 225 | } 226 | 227 | return lib, nil 228 | } 229 | 230 | var theLib *lib 231 | 232 | func init() { 233 | l, err := loadLib() 234 | if err != nil { 235 | panic(err) 236 | } 237 | theLib = &lib{ 238 | lib: l, 239 | } 240 | } 241 | 242 | func cBool(x bool) uintptr { 243 | if x { 244 | return 1 245 | } 246 | return 0 247 | } 248 | 249 | func RestartAppIfNecessary(appID uint32) bool { 250 | v, err := theLib.call(funcType_Bool_Int32, flatAPI_RestartAppIfNecessary, uintptr(appID)) 251 | if err != nil { 252 | panic(err) 253 | } 254 | return byte(v) != 0 255 | } 256 | 257 | func Init() error { 258 | var msg steamErrMsg 259 | v, err := theLib.call(funcType_Bool_Ptr, flatAPI_InitFlat, uintptr(unsafe.Pointer(&msg))) 260 | if err != nil { 261 | panic(err) 262 | } 263 | if ESteamAPIInitResult(v) != ESteamAPIInitResult_OK { 264 | return fmt.Errorf("steamworks: InitFlat failed: %d, %s", ESteamAPIInitResult(v), msg.String()) 265 | } 266 | return nil 267 | } 268 | 269 | func RunCallbacks() { 270 | if _, err := theLib.call(funcType_Void, flatAPI_RunCallbacks); err != nil { 271 | panic(err) 272 | } 273 | } 274 | 275 | func SteamApps() ISteamApps { 276 | v, err := theLib.call(funcType_Ptr, flatAPI_SteamApps) 277 | if err != nil { 278 | panic(err) 279 | } 280 | return steamApps(v) 281 | } 282 | 283 | type steamApps C.uintptr_t 284 | 285 | func (s steamApps) BGetDLCDataByIndex(iDLC int) (appID AppId_t, available bool, pchName string, success bool) { 286 | var name [4096]byte 287 | v, err := theLib.call(funcType_Int32_Ptr_Int32_Ptr_Ptr_Ptr_Int32, flatAPI_ISteamApps_BGetDLCDataByIndex, uintptr(s), uintptr(iDLC), uintptr(unsafe.Pointer(&appID)), uintptr(unsafe.Pointer(&available)), uintptr(unsafe.Pointer(&name[0])), uintptr(len(name))) 288 | if err != nil { 289 | panic(err) 290 | } 291 | return appID, available, C.GoString((*C.char)(unsafe.Pointer(&name[0]))), byte(v) != 0 292 | } 293 | 294 | func (s steamApps) BIsDlcInstalled(appID AppId_t) bool { 295 | v, err := theLib.call(funcType_Bool_Ptr_Int32, flatAPI_ISteamApps_BIsDlcInstalled, uintptr(s), uintptr(appID)) 296 | if err != nil { 297 | panic(err) 298 | } 299 | return byte(v) != 0 300 | } 301 | 302 | func (s steamApps) GetAppInstallDir(appID AppId_t) string { 303 | var path [4096]byte 304 | v, err := theLib.call(funcType_Int32_Ptr_Int32_Ptr_Int32, flatAPI_ISteamApps_GetAppInstallDir, uintptr(s), uintptr(appID), uintptr(unsafe.Pointer(&path[0])), uintptr(len(path))) 305 | if err != nil { 306 | panic(err) 307 | } 308 | return string(path[:uint32(v)-1]) 309 | } 310 | 311 | func (s steamApps) GetCurrentGameLanguage() string { 312 | v, err := theLib.call(funcType_Ptr_Ptr, flatAPI_ISteamApps_GetCurrentGameLanguage, uintptr(s)) 313 | if err != nil { 314 | panic(err) 315 | } 316 | return C.GoString(C.uintptrToChar(C.uintptr_t(v))) 317 | } 318 | 319 | func (s steamApps) GetDLCCount() int32 { 320 | v, err := theLib.call(funcType_Int32_Ptr, flatAPI_ISteamApps_GetDLCCount, uintptr(s)) 321 | if err != nil { 322 | panic(err) 323 | } 324 | return int32(v) 325 | } 326 | 327 | func SteamFriends() ISteamFriends { 328 | v, err := theLib.call(funcType_Ptr, flagAPI_SteamFriends) 329 | if err != nil { 330 | panic(err) 331 | } 332 | return steamFriends(v) 333 | } 334 | 335 | type steamFriends C.uintptr_t 336 | 337 | func (s steamFriends) GetPersonaName() string { 338 | v, err := theLib.call(funcType_Ptr_Ptr, flatAPI_ISteamFriends_GetPersonaName, uintptr(s)) 339 | if err != nil { 340 | panic(err) 341 | } 342 | return C.GoString(C.uintptrToChar(C.uintptr_t(v))) 343 | } 344 | 345 | func (s steamFriends) SetRichPresence(key, value string) bool { 346 | ckey := C.CString(key) 347 | defer C.free(unsafe.Pointer(ckey)) 348 | cvalue := C.CString(value) 349 | defer C.free(unsafe.Pointer(cvalue)) 350 | 351 | v, err := theLib.call(funcType_Bool_Ptr_Ptr_Ptr, flatAPI_ISteamFriends_SetRichPresence, uintptr(s), uintptr(unsafe.Pointer(ckey)), uintptr(unsafe.Pointer(cvalue))) 352 | if err != nil { 353 | panic(err) 354 | } 355 | 356 | return byte(v) != 0 357 | } 358 | 359 | func SteamInput() ISteamInput { 360 | v, err := theLib.call(funcType_Ptr, flatAPI_SteamInput) 361 | if err != nil { 362 | panic(err) 363 | } 364 | return steamInput(v) 365 | } 366 | 367 | type steamInput C.uintptr_t 368 | 369 | func (s steamInput) GetConnectedControllers() []InputHandle_t { 370 | var handles [_STEAM_INPUT_MAX_COUNT]InputHandle_t 371 | v, err := theLib.call(funcType_Int32_Ptr_Ptr, flatAPI_ISteamInput_GetConnectedControllers, uintptr(s), uintptr(unsafe.Pointer(&handles[0]))) 372 | if err != nil { 373 | panic(err) 374 | } 375 | return handles[:int(v)] 376 | } 377 | 378 | func (s steamInput) GetInputTypeForHandle(inputHandle InputHandle_t) ESteamInputType { 379 | v, err := theLib.call(funcType_Int32_Ptr_Int64, flatAPI_ISteamInput_GetInputTypeForHandle, uintptr(s), uintptr(inputHandle)) 380 | if err != nil { 381 | panic(err) 382 | } 383 | return ESteamInputType(v) 384 | } 385 | 386 | func (s steamInput) Init(bExplicitlyCallRunFrame bool) bool { 387 | var callRunFrame uintptr 388 | if bExplicitlyCallRunFrame { 389 | callRunFrame = 1 390 | } 391 | v, err := theLib.call(funcType_Bool_Ptr_Bool, flatAPI_ISteamInput_Init, uintptr(s), callRunFrame) 392 | if err != nil { 393 | panic(err) 394 | } 395 | return byte(v) != 0 396 | } 397 | 398 | func (s steamInput) RunFrame() { 399 | if _, err := theLib.call(funcType_Void_Ptr_Bool, flatAPI_ISteamInput_RunFrame, uintptr(s), 0); err != nil { 400 | panic(err) 401 | } 402 | } 403 | 404 | func SteamRemoteStorage() ISteamRemoteStorage { 405 | v, err := theLib.call(funcType_Ptr, flatAPI_SteamRemoteStorage) 406 | if err != nil { 407 | panic(err) 408 | } 409 | return steamRemoteStorage(v) 410 | } 411 | 412 | type steamRemoteStorage C.uintptr_t 413 | 414 | func (s steamRemoteStorage) FileWrite(file string, data []byte) bool { 415 | cfile := C.CString(file) 416 | defer C.free(unsafe.Pointer(cfile)) 417 | 418 | defer runtime.KeepAlive(data) 419 | 420 | v, err := theLib.call(funcType_Bool_Ptr_Ptr_Ptr_Int32, flatAPI_ISteamRemoteStorage_FileWrite, uintptr(s), uintptr(unsafe.Pointer(cfile)), uintptr(unsafe.Pointer(&data[0])), uintptr(len(data))) 421 | if err != nil { 422 | panic(err) 423 | } 424 | return byte(v) != 0 425 | } 426 | 427 | func (s steamRemoteStorage) FileRead(file string, data []byte) int32 { 428 | cfile := C.CString(file) 429 | defer C.free(unsafe.Pointer(cfile)) 430 | 431 | defer runtime.KeepAlive(data) 432 | 433 | v, err := theLib.call(funcType_Int32_Ptr_Ptr_Ptr_Int32, flatAPI_ISteamRemoteStorage_FileRead, uintptr(s), uintptr(unsafe.Pointer(cfile)), uintptr(unsafe.Pointer(&data[0])), uintptr(len(data))) 434 | if err != nil { 435 | panic(err) 436 | } 437 | return int32(v) 438 | } 439 | 440 | func (s steamRemoteStorage) FileDelete(file string) bool { 441 | cfile := C.CString(file) 442 | defer C.free(unsafe.Pointer(cfile)) 443 | 444 | v, err := theLib.call(funcType_Bool_Ptr_Ptr, flatAPI_ISteamRemoteStorage_FileDelete, uintptr(s), uintptr(unsafe.Pointer(cfile))) 445 | if err != nil { 446 | panic(err) 447 | } 448 | return byte(v) != 0 449 | } 450 | 451 | func (s steamRemoteStorage) GetFileSize(file string) int32 { 452 | cfile := C.CString(file) 453 | defer C.free(unsafe.Pointer(cfile)) 454 | 455 | v, err := theLib.call(funcType_Int32_Ptr, flatAPI_ISteamRemoteStorage_GetFileSize, uintptr(s), uintptr(unsafe.Pointer(cfile))) 456 | if err != nil { 457 | panic(err) 458 | } 459 | return int32(v) 460 | } 461 | 462 | func SteamUser() ISteamUser { 463 | v, err := theLib.call(funcType_Ptr, flatAPI_SteamUser) 464 | if err != nil { 465 | panic(err) 466 | } 467 | return steamUser(v) 468 | } 469 | 470 | type steamUser C.uintptr_t 471 | 472 | func (s steamUser) GetSteamID() CSteamID { 473 | v, err := theLib.call(funcType_Int64_Ptr, flatAPI_ISteamUser_GetSteamID, uintptr(s)) 474 | if err != nil { 475 | panic(err) 476 | } 477 | return CSteamID(v) 478 | } 479 | 480 | func SteamUserStats() ISteamUserStats { 481 | v, err := theLib.call(funcType_Ptr, flatAPI_SteamUserStats) 482 | if err != nil { 483 | panic(err) 484 | } 485 | return steamUserStats(v) 486 | } 487 | 488 | type steamUserStats C.uintptr_t 489 | 490 | func (s steamUserStats) GetAchievement(name string) (achieved, success bool) { 491 | cname := C.CString(name) 492 | defer C.free(unsafe.Pointer(cname)) 493 | 494 | v, err := theLib.call(funcType_Bool_Ptr_Ptr_Ptr, flatAPI_ISteamUserStats_GetAchievement, uintptr(s), uintptr(unsafe.Pointer(cname)), uintptr(unsafe.Pointer(&achieved))) 495 | if err != nil { 496 | panic(err) 497 | } 498 | success = byte(v) != 0 499 | 500 | return 501 | } 502 | 503 | func (s steamUserStats) SetAchievement(name string) bool { 504 | cname := C.CString(name) 505 | defer C.free(unsafe.Pointer(cname)) 506 | 507 | v, err := theLib.call(funcType_Bool_Ptr_Ptr, flatAPI_ISteamUserStats_SetAchievement, uintptr(s), uintptr(unsafe.Pointer(cname))) 508 | if err != nil { 509 | panic(err) 510 | } 511 | 512 | return byte(v) != 0 513 | } 514 | 515 | func (s steamUserStats) ClearAchievement(name string) bool { 516 | cname := C.CString(name) 517 | defer C.free(unsafe.Pointer(cname)) 518 | 519 | v, err := theLib.call(funcType_Bool_Ptr_Ptr, flatAPI_ISteamUserStats_ClearAchievement, uintptr(s), uintptr(unsafe.Pointer(cname))) 520 | if err != nil { 521 | panic(err) 522 | } 523 | 524 | return byte(v) != 0 525 | } 526 | 527 | func (s steamUserStats) StoreStats() bool { 528 | v, err := theLib.call(funcType_Bool_Ptr, flatAPI_ISteamUserStats_StoreStats, uintptr(s)) 529 | if err != nil { 530 | panic(err) 531 | } 532 | 533 | return byte(v) != 0 534 | } 535 | 536 | func SteamUtils() ISteamUtils { 537 | v, err := theLib.call(funcType_Ptr, flatAPI_SteamUtils) 538 | if err != nil { 539 | panic(err) 540 | } 541 | return steamUtils(v) 542 | } 543 | 544 | type steamUtils C.uintptr_t 545 | 546 | func (s steamUtils) IsSteamRunningOnSteamDeck() bool { 547 | v, err := theLib.call(funcType_Bool_Ptr, flatAPI_ISteamUtils_IsSteamRunningOnSteamDeck, uintptr(s)) 548 | if err != nil { 549 | panic(err) 550 | } 551 | return byte(v) != 0 552 | } 553 | 554 | func (s steamUtils) ShowFloatingGamepadTextInput(keyboardMode EFloatingGamepadTextInputMode, textFieldXPosition, textFieldYPosition, textFieldWidth, textFieldHeight int32) bool { 555 | v, err := theLib.call(funcType_Bool_Ptr_Int32_Int32_Int32_Int32_Int32, flatAPI_ISteamUtils_ShowFloatingGamepadTextInput, uintptr(s), uintptr(keyboardMode), uintptr(textFieldXPosition), uintptr(textFieldYPosition), uintptr(textFieldWidth), uintptr(textFieldHeight)) 556 | if err != nil { 557 | panic(err) 558 | } 559 | return byte(v) != 0 560 | } 561 | -------------------------------------------------------------------------------- /steamworks_windows.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // SPDX-FileCopyrightText: 2021 The go-steamworks Authors 3 | 4 | package steamworks 5 | 6 | import ( 7 | "fmt" 8 | "runtime" 9 | "unsafe" 10 | 11 | "golang.org/x/sys/windows" 12 | ) 13 | 14 | const is32Bit = unsafe.Sizeof(int(0)) == 4 15 | 16 | func cStringToGoString(v uintptr, sizeHint int) string { 17 | bs := make([]byte, 0, sizeHint) 18 | for i := int32(0); ; i++ { 19 | b := *(*byte)(unsafe.Pointer(v)) 20 | v += unsafe.Sizeof(byte(0)) 21 | if b == 0 { 22 | break 23 | } 24 | bs = append(bs, b) 25 | } 26 | return string(bs) 27 | } 28 | 29 | type dll struct { 30 | d *windows.LazyDLL 31 | procs map[string]*windows.LazyProc 32 | } 33 | 34 | func (d *dll) call(name string, args ...uintptr) (uintptr, error) { 35 | if d.procs == nil { 36 | d.procs = map[string]*windows.LazyProc{} 37 | } 38 | if _, ok := d.procs[name]; !ok { 39 | d.procs[name] = d.d.NewProc(name) 40 | } 41 | r, _, err := d.procs[name].Call(args...) 42 | if err != nil { 43 | errno, ok := err.(windows.Errno) 44 | if !ok { 45 | return r, err 46 | } 47 | if errno != 0 { 48 | return r, err 49 | } 50 | } 51 | return r, nil 52 | } 53 | 54 | func loadDLL() (*dll, error) { 55 | dllName := "steam_api.dll" 56 | if !is32Bit { 57 | dllName = "steam_api64.dll" 58 | } 59 | 60 | return &dll{ 61 | d: windows.NewLazyDLL(dllName), 62 | }, nil 63 | } 64 | 65 | var theDLL *dll 66 | 67 | func init() { 68 | dll, err := loadDLL() 69 | if err != nil { 70 | panic(err) 71 | } 72 | theDLL = dll 73 | } 74 | 75 | func RestartAppIfNecessary(appID uint32) bool { 76 | v, err := theDLL.call(flatAPI_RestartAppIfNecessary, uintptr(appID)) 77 | if err != nil { 78 | panic(err) 79 | } 80 | return byte(v) != 0 81 | } 82 | 83 | func Init() error { 84 | var msg steamErrMsg 85 | v, err := theDLL.call(flatAPI_InitFlat, uintptr(unsafe.Pointer(&msg[0]))) 86 | if err != nil { 87 | panic(err) 88 | } 89 | if ESteamAPIInitResult(v) != ESteamAPIInitResult_OK { 90 | return fmt.Errorf("steamworks: InitFlat failed: %d, %s", ESteamAPIInitResult(v), msg.String()) 91 | } 92 | return nil 93 | } 94 | 95 | func RunCallbacks() { 96 | if _, err := theDLL.call(flatAPI_RunCallbacks); err != nil { 97 | panic(err) 98 | } 99 | } 100 | 101 | func SteamApps() ISteamApps { 102 | v, err := theDLL.call(flatAPI_SteamApps) 103 | if err != nil { 104 | panic(err) 105 | } 106 | return steamApps(v) 107 | } 108 | 109 | type steamApps uintptr 110 | 111 | func (s steamApps) BGetDLCDataByIndex(iDLC int) (appID AppId_t, available bool, pchName string, success bool) { 112 | var name [4096]byte 113 | v, err := theDLL.call(flatAPI_ISteamApps_BGetDLCDataByIndex, uintptr(s), uintptr(iDLC), uintptr(unsafe.Pointer(&appID)), uintptr(unsafe.Pointer(&available)), uintptr(unsafe.Pointer(&name[0])), uintptr(len(name))) 114 | if err != nil { 115 | panic(err) 116 | } 117 | return appID, available, cStringToGoString(v, len(name)), byte(v) != 0 118 | } 119 | 120 | func (s steamApps) BIsDlcInstalled(appID AppId_t) bool { 121 | v, err := theDLL.call(flatAPI_ISteamApps_BIsDlcInstalled, uintptr(s), uintptr(appID)) 122 | if err != nil { 123 | panic(err) 124 | } 125 | return byte(v) != 0 126 | } 127 | 128 | func (s steamApps) GetAppInstallDir(appID AppId_t) string { 129 | var path [4096]byte 130 | v, err := theDLL.call(flatAPI_ISteamApps_GetAppInstallDir, uintptr(s), uintptr(appID), uintptr(unsafe.Pointer(&path[0])), uintptr(len(path))) 131 | if err != nil { 132 | panic(err) 133 | } 134 | return string(path[:uint32(v)-1]) 135 | } 136 | 137 | func (s steamApps) GetCurrentGameLanguage() string { 138 | v, err := theDLL.call(flatAPI_ISteamApps_GetCurrentGameLanguage, uintptr(s)) 139 | if err != nil { 140 | panic(err) 141 | } 142 | return cStringToGoString(v, 256) 143 | } 144 | 145 | func (s steamApps) GetDLCCount() int32 { 146 | v, err := theDLL.call(flatAPI_ISteamApps_GetDLCCount, uintptr(s)) 147 | if err != nil { 148 | panic(err) 149 | } 150 | return int32(v) 151 | } 152 | 153 | func SteamFriends() ISteamFriends { 154 | v, err := theDLL.call(flagAPI_SteamFriends) 155 | if err != nil { 156 | panic(err) 157 | } 158 | return steamFriends(v) 159 | } 160 | 161 | type steamFriends uintptr 162 | 163 | func (s steamFriends) GetPersonaName() string { 164 | v, err := theDLL.call(flatAPI_ISteamFriends_GetPersonaName, uintptr(s)) 165 | if err != nil { 166 | panic(err) 167 | } 168 | return cStringToGoString(v, 64) 169 | } 170 | 171 | func (s steamFriends) SetRichPresence(key, value string) bool { 172 | ckey := append([]byte(key), 0) 173 | defer runtime.KeepAlive(ckey) 174 | cvalue := append([]byte(value), 0) 175 | defer runtime.KeepAlive(cvalue) 176 | 177 | v, err := theDLL.call(flatAPI_ISteamFriends_SetRichPresence, uintptr(s), uintptr(unsafe.Pointer(&ckey[0])), uintptr(unsafe.Pointer(&cvalue[0]))) 178 | if err != nil { 179 | panic(err) 180 | } 181 | return byte(v) != 0 182 | } 183 | 184 | func SteamInput() ISteamInput { 185 | v, err := theDLL.call(flatAPI_SteamInput) 186 | if err != nil { 187 | panic(err) 188 | } 189 | return steamInput(v) 190 | } 191 | 192 | type steamInput uintptr 193 | 194 | func (s steamInput) GetConnectedControllers() []InputHandle_t { 195 | var handles [_STEAM_INPUT_MAX_COUNT]InputHandle_t 196 | v, err := theDLL.call(flatAPI_ISteamInput_GetConnectedControllers, uintptr(s), uintptr(unsafe.Pointer(&handles[0]))) 197 | if err != nil { 198 | panic(err) 199 | } 200 | return handles[:int(v)] 201 | } 202 | 203 | func (s steamInput) GetInputTypeForHandle(inputHandle InputHandle_t) ESteamInputType { 204 | v, err := theDLL.call(flatAPI_ISteamInput_GetInputTypeForHandle, uintptr(s), uintptr(inputHandle)) 205 | if err != nil { 206 | panic(err) 207 | } 208 | return ESteamInputType(v) 209 | } 210 | 211 | func (s steamInput) Init(bExplicitlyCallRunFrame bool) bool { 212 | var callRunFrame uintptr 213 | if bExplicitlyCallRunFrame { 214 | callRunFrame = 1 215 | } 216 | // The error value seems unreliable. 217 | v, _ := theDLL.call(flatAPI_ISteamInput_Init, uintptr(s), callRunFrame) 218 | return byte(v) != 0 219 | } 220 | 221 | func (s steamInput) RunFrame() { 222 | if _, err := theDLL.call(flatAPI_ISteamInput_RunFrame, uintptr(s), 0); err != nil { 223 | panic(err) 224 | } 225 | } 226 | 227 | func SteamRemoteStorage() ISteamRemoteStorage { 228 | v, err := theDLL.call(flatAPI_SteamRemoteStorage) 229 | if err != nil { 230 | panic(err) 231 | } 232 | return steamRemoteStorage(v) 233 | } 234 | 235 | type steamRemoteStorage uintptr 236 | 237 | func (s steamRemoteStorage) FileWrite(file string, data []byte) bool { 238 | cfile := append([]byte(file), 0) 239 | defer runtime.KeepAlive(cfile) 240 | 241 | defer runtime.KeepAlive(data) 242 | 243 | v, err := theDLL.call(flatAPI_ISteamRemoteStorage_FileWrite, uintptr(s), uintptr(unsafe.Pointer(&cfile[0])), uintptr(unsafe.Pointer(&data[0])), uintptr(len(data))) 244 | if err != nil { 245 | panic(err) 246 | } 247 | 248 | return byte(v) != 0 249 | } 250 | 251 | func (s steamRemoteStorage) FileRead(file string, data []byte) int32 { 252 | cfile := append([]byte(file), 0) 253 | defer runtime.KeepAlive(cfile) 254 | 255 | defer runtime.KeepAlive(data) 256 | 257 | v, err := theDLL.call(flatAPI_ISteamRemoteStorage_FileRead, uintptr(s), uintptr(unsafe.Pointer(&cfile[0])), uintptr(unsafe.Pointer(&data[0])), uintptr(len(data))) 258 | if err != nil { 259 | panic(err) 260 | } 261 | 262 | return int32(v) 263 | } 264 | 265 | func (s steamRemoteStorage) FileDelete(file string) bool { 266 | cfile := append([]byte(file), 0) 267 | defer runtime.KeepAlive(cfile) 268 | 269 | v, err := theDLL.call(flatAPI_ISteamRemoteStorage_FileDelete, uintptr(s), uintptr(unsafe.Pointer(&cfile[0]))) 270 | if err != nil { 271 | panic(err) 272 | } 273 | 274 | return byte(v) != 0 275 | } 276 | 277 | func (s steamRemoteStorage) GetFileSize(file string) int32 { 278 | cfile := append([]byte(file), 0) 279 | defer runtime.KeepAlive(cfile) 280 | 281 | v, err := theDLL.call(flatAPI_ISteamRemoteStorage_GetFileSize, uintptr(s), uintptr(unsafe.Pointer(&cfile[0]))) 282 | if err != nil { 283 | panic(err) 284 | } 285 | 286 | return int32(v) 287 | } 288 | 289 | func SteamUser() ISteamUser { 290 | v, err := theDLL.call(flatAPI_SteamUser) 291 | if err != nil { 292 | panic(err) 293 | } 294 | return steamUser(v) 295 | } 296 | 297 | type steamUser uintptr 298 | 299 | func (s steamUser) GetSteamID() CSteamID { 300 | if is32Bit { 301 | // On 32bit machines, syscall cannot treat a returned value as 64bit. 302 | panic("GetSteamID is not implemented on 32bit Windows") 303 | } 304 | v, err := theDLL.call(flatAPI_ISteamUser_GetSteamID, uintptr(s)) 305 | if err != nil { 306 | panic(err) 307 | } 308 | return CSteamID(v) 309 | } 310 | 311 | func SteamUserStats() ISteamUserStats { 312 | v, err := theDLL.call(flatAPI_SteamUserStats) 313 | if err != nil { 314 | panic(err) 315 | } 316 | return steamUserStats(v) 317 | } 318 | 319 | type steamUserStats uintptr 320 | 321 | func (s steamUserStats) GetAchievement(name string) (achieved, success bool) { 322 | cname := append([]byte(name), 0) 323 | defer runtime.KeepAlive(cname) 324 | 325 | v, err := theDLL.call(flatAPI_ISteamUserStats_GetAchievement, uintptr(s), uintptr(unsafe.Pointer(&cname[0])), uintptr(unsafe.Pointer(&achieved))) 326 | if err != nil { 327 | panic(err) 328 | } 329 | 330 | success = byte(v) != 0 331 | return 332 | } 333 | 334 | func (s steamUserStats) SetAchievement(name string) bool { 335 | cname := append([]byte(name), 0) 336 | defer runtime.KeepAlive(cname) 337 | 338 | v, err := theDLL.call(flatAPI_ISteamUserStats_SetAchievement, uintptr(s), uintptr(unsafe.Pointer(&cname[0]))) 339 | if err != nil { 340 | panic(err) 341 | } 342 | 343 | return byte(v) != 0 344 | } 345 | 346 | func (s steamUserStats) ClearAchievement(name string) bool { 347 | cname := append([]byte(name), 0) 348 | defer runtime.KeepAlive(cname) 349 | 350 | v, err := theDLL.call(flatAPI_ISteamUserStats_ClearAchievement, uintptr(s), uintptr(unsafe.Pointer(&cname[0]))) 351 | if err != nil { 352 | panic(err) 353 | } 354 | 355 | return byte(v) != 0 356 | } 357 | 358 | func (s steamUserStats) StoreStats() bool { 359 | v, err := theDLL.call(flatAPI_ISteamUserStats_StoreStats, uintptr(s)) 360 | if err != nil { 361 | panic(err) 362 | } 363 | 364 | return byte(v) != 0 365 | } 366 | 367 | func SteamUtils() ISteamUtils { 368 | v, err := theDLL.call(flatAPI_SteamUtils) 369 | if err != nil { 370 | panic(err) 371 | } 372 | return steamUtils(v) 373 | } 374 | 375 | type steamUtils uintptr 376 | 377 | func (s steamUtils) IsSteamRunningOnSteamDeck() bool { 378 | v, err := theDLL.call(flatAPI_ISteamUtils_IsSteamRunningOnSteamDeck, uintptr(s)) 379 | if err != nil { 380 | panic(err) 381 | } 382 | 383 | return byte(v) != 0 384 | } 385 | 386 | func (s steamUtils) ShowFloatingGamepadTextInput(keyboardMode EFloatingGamepadTextInputMode, textFieldXPosition, textFieldYPosition, textFieldWidth, textFieldHeight int32) bool { 387 | v, err := theDLL.call(flatAPI_ISteamUtils_ShowFloatingGamepadTextInput, uintptr(s), uintptr(keyboardMode), uintptr(textFieldXPosition), uintptr(textFieldYPosition), uintptr(textFieldWidth), uintptr(textFieldHeight)) 388 | if err != nil { 389 | panic(err) 390 | } 391 | return byte(v) != 0 392 | } 393 | --------------------------------------------------------------------------------