// Copyright 2024 The Ebitengine Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build ignore // This program generates platform-specific controller dbs. It can be invoked by running // go generate package main import ( "bytes" _ "embed" "fmt" "log" "os" "path/filepath" "strings" "text/template" ) const license = `// Copyright 2024 The Ebitengine Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ` // gamecontrollerdb.txt is downloaded at https://github.com/mdqinc/SDL_GameControllerDB. // To update the database file, run: // // curl --location --remote-name https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/master/gamecontrollerdb.txt //go:embed gamecontrollerdb.txt var gamecontrollerdb_txt []byte // additional GLFW gamepads for Windows var additionalGLFWGamepads = []byte(` 78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, 78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, 78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, 78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, 78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, 78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, 78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, `) //go:generate go run gen_dbs.go func main() { // Follow the standard comment rule (https://pkg.go.dev/cmd/go#hdr-Generate_Go_files_by_processing_source). doNotEdit := "// Code generated by gen_dbs.go using 'go generate'. DO NOT EDIT." type gamePadPlatform struct { genFileName string buildTag string } supported := map[string]gamePadPlatform{ "Windows": { genFileName: "windows", buildTag: "//go:build windows && !microsoftgdk", }, "Mac OS X": { genFileName: "macos_darwin", buildTag: "//go:build darwin && !ios", }, "Linux": { genFileName: "linbsd", buildTag: "//go:build (freebsd || (linux && !android) || netbsd || openbsd) && !nintendosdk && !playstation5", }, "iOS": { genFileName: "ios", buildTag: "//go:build ios", }, "Android": { genFileName: "android", buildTag: "//go:build android", }, } tmpl, err := template.ParseFiles("gamepaddb_tmpl.tpl") if err != nil { log.Fatal(err) } controllerDbs := splitControllerDb(gamecontrollerdb_txt) for sdlName, platform := range supported { path := filepath.Join("internal", "gamepaddb", fmt.Sprintf("db_%s.go", platform.genFileName)) f, err := os.Create(path) if err != nil { log.Fatal(err) } defer f.Close() controllerDb, ok := controllerDbs[sdlName] if !ok { log.Fatalf("failed to find controller db for platform %s in gamecontrollerdb_txt", sdlName) } err = tmpl.Execute(f, struct { License string DoNotEdit string BuildTag string SDLPlatform string ControllerBytes []byte GLFWGamePads []byte }{ License: license, DoNotEdit: doNotEdit, BuildTag: platform.buildTag, SDLPlatform: sdlName, ControllerBytes: controllerDb, GLFWGamePads: additionalGLFWGamepads, }) if err != nil { log.Fatal(err) } } } // splitControllerDb maps the game controller db into respective controller definition byte slices for // each platform func splitControllerDb(controllerDb []byte) map[string][]byte { dbs := map[string][]byte{} // split at #, skip first 3 header chunks chunks := bytes.Split(controllerDb, []byte{'#'})[3:] for _, chunk := range chunks { // find each platform string lines := bytes.Split(chunk, []byte{'\n'}) if len(lines) == 0 { continue } platform := strings.TrimSpace(string(lines[0])) // count platform string len + space + newline bytesRead := len(lines[0]) + 1 dbs[platform] = chunk[bytesRead:] } return dbs }