diff --git a/input.go b/input.go index 5387eac7d..93a7cd500 100644 --- a/input.go +++ b/input.go @@ -335,7 +335,10 @@ func IsStandardGamepadLayoutAvailable(id GamepadID) bool { // // UpdateStandardGamepadLayoutMappings works atomically. If an error happens, nothing is updated. func UpdateStandardGamepadLayoutMappings(mappings string) (bool, error) { - return gamepaddb.Update([]byte(mappings)) + if err := gamepaddb.Update([]byte(mappings)); err != nil { + return false, err + } + return true, nil } // TouchID represents a touch's identifier. diff --git a/internal/gamepaddb/gamepaddb.go b/internal/gamepaddb/gamepaddb.go index 547c0b01b..38705d81a 100644 --- a/internal/gamepaddb/gamepaddb.go +++ b/internal/gamepaddb/gamepaddb.go @@ -92,10 +92,10 @@ var additionalGLFWGamepads = []byte(` `) func init() { - if _, err := Update(gamecontrollerdbTxt); err != nil { + if err := Update(gamecontrollerdbTxt); err != nil { panic(err) } - if _, err := Update(additionalGLFWGamepads); err != nil { + if err := Update(additionalGLFWGamepads); err != nil { panic(err) } } @@ -139,11 +139,18 @@ func parseLine(line string, platform platform) (id string, name string, buttons return "", "", nil, nil, nil } tokens := strings.Split(line, ",") + if len(tokens) < 2 { + return "", "", nil, nil, fmt.Errorf("gamepaddb: syntax error") + } + for _, token := range tokens[2:] { if len(token) == 0 { continue } tks := strings.Split(token, ":") + if len(tks) < 2 { + return "", "", nil, nil, fmt.Errorf("gamepaddb: syntax error") + } // Note that the platform part is listed in the definition of SDL_GetPlatform. if tks[0] == "platform" { @@ -501,7 +508,7 @@ func IsButtonPressed(id string, button StandardButton, state GamepadState) bool // The string must be in the format of SDL_GameControllerDB. // // Update works atomically. If an error happens, nothing is updated. -func Update(mappingData []byte) (bool, error) { +func Update(mappingData []byte) error { mappingsM.Lock() defer mappingsM.Unlock() @@ -519,11 +526,11 @@ func Update(mappingData []byte) (bool, error) { for { line, err := r.ReadString('\n') if err != nil && err != io.EOF { - return false, err + return err } id, name, buttons, axes, err1 := parseLine(line, currentPlatform) if err1 != nil { - return false, err1 + return err1 } if id != "" { lines = append(lines, parsedLine{ @@ -544,7 +551,7 @@ func Update(mappingData []byte) (bool, error) { gamepadAxisMappings[l.id] = l.axes } - return true, nil + return nil } func addAndroidDefaultMappings(id string) bool { diff --git a/internal/gamepaddb/gamepaddb_test.go b/internal/gamepaddb/gamepaddb_test.go new file mode 100644 index 000000000..42f2149cb --- /dev/null +++ b/internal/gamepaddb/gamepaddb_test.go @@ -0,0 +1,67 @@ +// Copyright 2022 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. + +package gamepaddb_test + +import ( + "testing" + + "github.com/hajimehoshi/ebiten/v2/internal/gamepaddb" +) + +func TestUpdate(t *testing.T) { + cases := []struct { + Input string + Err bool + }{ + { + Input: "", + Err: false, + }, + { + Input: "{}", + Err: true, + }, + { + Input: "00000000000000000000000000000000", + Err: true, + }, + { + Input: "00000000000000000000000000000000,foo", + Err: false, + }, + { + Input: "00000000000000000000000000000000,foo,platform", + Err: true, + }, + { + Input: "00000000000000000000000000000000,foo,pltform:Foo", + Err: true, + }, + { + Input: "00000000000000000000000000000000,foo,platform:Windows", + Err: false, + }, + } + + for _, c := range cases { + err := gamepaddb.Update([]byte(c.Input)) + if err == nil && c.Err { + t.Errorf("Update(%q) should return an error but not", c.Input) + } + if err != nil && !c.Err { + t.Errorf("Update(%q) should not return an error but returned %v", c.Input, err) + } + } +}