// Copyright (C) 2019 ston1th package encoder import ( "bufio" "io" "strconv" "strings" "git.giftfish.de/ston1th/duckycode/pkg/keys" "git.giftfish.de/ston1th/duckycode/pkg/lang" ) type Encoder struct { writer io.Writer language string defaultDelay int delayOverride bool loop int repeat bool } func NewEncoder(w io.Writer, language string) (*Encoder, error) { err := lang.Exists(language) return &Encoder{w, language, 0, false, 0, false}, err } func (e *Encoder) Encode(r io.Reader) (err error) { var buf []byte scanner := bufio.NewScanner(r) line := 1 for scanner.Scan() { if err = scanner.Err(); err != nil { return } s := strings.SplitN(scanner.Text(), " ", 2) switch len(s) { case 1: if b := e.instruction(strings.TrimSpace(s[0])); b != nil { buf = append(buf, append(b, byte(0))...) } case 2: buf = append(buf, e.instructionData(strings.TrimSpace(s[0]), strings.TrimSpace(s[1]))...) } line++ if !e.delayOverride && e.defaultDelay > 0 { delayCounter := e.defaultDelay for delayCounter > 0 { buf = append(buf, byte(0)) if delayCounter > 255 { buf = append(buf, byte(255)) delayCounter -= 255 } else { buf = append(buf, byte(delayCounter)) delayCounter = 0 } } } } _, err = e.writer.Write(buf) return } func (e *Encoder) instruction(in string) []byte { if b, ok := keys.K["KEY_"+in]; ok { return []byte{b} } switch in { case "", "REM": return nil case "ESCAPE": return e.instruction("ESC") case "DEL": return e.instruction("DELETE") case "BREAK": return e.instruction("PAUSE") case "CONTROL": return e.instruction("CTRL") case "DOWNARROW": return e.instruction("DOWM") case "UPARROW": return e.instruction("UP") case "LEFTARROW": return e.instruction("LEFT") case "RIGHTARROW": return e.instruction("RIGHT") case "MENU": return e.instruction("APP") case "WINDOWS": return e.instruction("GUI") case "PLAY": return e.instruction("MEDIA_PLAY_PAUSE") case "MUTE": return e.instruction("MEDIA_MUTE") case "VOLUMEUP": return e.instruction("MEDIA_VOLUME_INC") case "VOLUMEDOWN": return e.instruction("MEDIAVOLUME_DEC") case "SCROLLLOCK": return e.instruction("SCROLL_LOCK") case "NUMLOCK": return e.instruction("NUM_LOCK") case "CAPSLOCK": return e.instruction("CAPS_LOCK") case "CTRL": return e.instruction("LEFT_CTRL") case "ALT": return e.instruction("LEFT_ALT") case "SHIFT": return e.instruction("LEFT_SHIFT") case "ALT-SHIFT": return append(e.instruction("LEFT_ALT"), keys.K["MODIFIERKEY_LEFT_ALT"]|keys.K["MODIFIERKEY_SHIFT"]) case "ALT-TAB": return append(e.instruction("TAB"), keys.K["MODIFIERKEY_LEFT_ALT"]) } return e.encodeRune(rune(in[0]))[:1] } func (e *Encoder) instructionData(in string, data string) (b []byte) { switch in { case "", "REM": return nil case "DEFAULT_DELAY", "DEFAULTDELAY": delay, err := strconv.Atoi(data) if err == nil { e.defaultDelay = delay e.delayOverride = true } return nil case "DELAY": delay, err := strconv.Atoi(data) if err != nil { return nil } for delay > 0 { b = append(b, byte(0)) if delay > 255 { b = append(b, byte(255)) delay -= 255 } else { b = append(b, byte(delay)) delay = 0 } } e.delayOverride = true return case "STRING": for _, r := range data { enc := e.encodeRune(r) if len(enc)%2 != 0 { enc = append(enc, byte(0)) } b = append(b, enc...) } return case "CONTROL", "CTRL": return append(e.instruction(data), keys.K["MODIFIERKEY_CTRL"]) case "ALT": return append(e.instruction(data), keys.K["MODIFIERKEY_ALT"]) case "SHIFT": return append(e.instruction(data), keys.K["MODIFIERKEY_SHIFT"]) case "CTRL-ALT": return append(e.instruction(data), keys.K["MODIFIERKEY_CTRL"]|keys.K["MODIFIERKEY_ALT"]) case "CTRL-SHIFT": return append(e.instruction(data), keys.K["MODIFIERKEY_CTRL"]|keys.K["MODIFIERKEY_SHIFT"]) case "COMMAND-OPTION": return append(e.instruction(data), keys.K["MODIFIERKEY_LEFT_GUI"]|keys.K["MODIFIERKEY_ALT"]) case "ALT-SHIFT": return append(e.instruction(data), keys.K["MODIFIERKEY_LEFT_ALT"]|keys.K["MODIFIERKEY_SHIFT"]) case "WINDOWS", "GUI", "COMMAND": return append(e.instruction(data), keys.K["MODIFIERKEY_LEFT_GUI"]) } return append(e.instruction(data), byte(0)) } func (e *Encoder) encodeRune(r rune) []byte { var code string hex := strings.ToUpper(strconv.FormatUint(uint64(r), 16)) switch { case r < 128: code = "ASCII_" + hex case r < 256: code = "ISO_8859_1_" + hex default: code = "UNICODE_" + hex } if b, ok := keys.K[code]; ok { return []byte{b} } if k, ok := lang.L[e.language][code]; ok { if k.Mod != 0 { return []byte{k.Key, k.Mod} } return []byte{k.Key} } return nil }