├── .gitignore ├── .gitmodules ├── README.md ├── bin └── mfkey32v2 ├── build.sh ├── config ├── config.go ├── config.yaml ├── files.go └── map.go ├── crc16 └── crc.go ├── dataTab.go ├── eml2dump └── eml2dump.go ├── macros ├── getLog.cmds ├── mf_auth.cmds └── send.cmds ├── main.go ├── nonces └── nonce.go ├── screenshots ├── Data-Diff.png ├── Serial-Terminal.png └── Tag-Editor.png ├── serial.go ├── slotFunctions.go ├── slotTab.go ├── terminalTab.go └── xmodem └── xmodem.go /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | ToDo.txt 3 | moc* 4 | .idea/ 5 | xmodem.c_back 6 | .DS_Store 7 | deploy/ 8 | *.zip 9 | xmutil 10 | *.dump 11 | test* 12 | windows 13 | linux 14 | darwin 15 | macros 16 | bin 17 | *.cmds 18 | *.exe 19 | mfkey32v2* 20 | chamgo-qt.icns 21 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/go.bug.st/serial.v1"] 2 | path = vendor/go.bug.st/serial.v1 3 | url = https://go.bug.st/git/serial.v1/enumerator.git 4 | [submodule "vendor/go.bug.st/serial.v1/enumerator"] 5 | path = vendor/go.bug.st/serial.v1/enumerator 6 | url = https://go.bug.st/git/serial.v1.git 7 | [submodule "vendor/github.com/creack/goselect"] 8 | path = vendor/github.com/creack/goselect 9 | url = https://github.com/creack/goselect 10 | [submodule "vendor/golang.org/x/sys"] 11 | path = vendor/golang.org/x/sys 12 | url = https://go.googlesource.com/sys -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## chamgo-qt 2 | QT based GUI for Chameleon-RevE-Rebooted & Chameleon RevG - written in Golang 3 | 4 | what works in general (on RevE-Rebooted & RevG): 5 | - USB-Device detection 6 | - Serial connection 7 | - display /edit Slot-Config 8 | - Serial Terminal 9 | - upload / download dump 10 | - get & decode detection-nonces for the use with mfkey32v2 (RevE-Rebooted) 11 | - logdownload (RevG - untested) 12 | - implementation of crc16 / BCC 13 | - display RSSI 14 | - integration of mfkey32v2 (RevE-Rebooted - workaround with binary) 15 | - diff/edit Dump-/Slot-Data 16 | 17 | what is missing: 18 | - logmode=live (RevG) 19 | 20 | to install/create the qt-bindings follow the instuctions in the wiki: https://github.com/therecipe/qt/wiki 21 | 22 | there are also some pre-compiled binaries in the ['release-section'](https://github.com/WolfgangMau/chamgo-qt/releases) 23 | 24 | ### Screenshots 25 | #### Serial-Terminal 26 | ![Serial-Terminal](https://github.com/WolfgangMau/chamgo-qt/blob/master/screenshots/Serial-Terminal.png) 27 | 28 | #### Tag-Editor 29 | ![Tag-Editor](https://github.com/WolfgangMau/chamgo-qt/blob/master/screenshots/Tag-Editor.png) 30 | 31 | #### Data-Diff 32 | ![Tag-Editor](https://github.com/WolfgangMau/chamgo-qt/blob/master/screenshots/Data-Diff.png) 33 | -------------------------------------------------------------------------------- /bin/mfkey32v2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WolfgangMau/chamgo-qt/59dadc289a327f7eb4a54dd8f37268025dfae012/bin/mfkey32v2 -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -rf deploy/* 3 | # 4 | echo "Linux" 5 | docker pull therecipe/qt:linux 6 | qtdeploy -docker build linux 7 | #zip -r deploy/chamgo-qt-linux.zip deploy/linux 8 | 9 | echo "windows64" 10 | docker pull therecipe/qt:windows_64_static 11 | qtdeploy -docker build windows_64_static 12 | mv deploy/windows deploy/windows64 13 | #zip -r deploy/chamgo-qt-win64.zip deploy/windows64 14 | 15 | echo "windows32" 16 | docker pull therecipe/qt:windows_32_static 17 | qtdeploy -docker build windows_32_static 18 | rm -rf deploy/windows32 19 | mv deploy/windows deploy/windows32 20 | #zip -r deploy/chamgo-qt-win32.zip deploy/windows32 21 | 22 | echo "darwin" 23 | qtdeploy 24 | cp chamgo-qt.icns deploy/darwin/chamgo-qt.app/Contents/Resources/ 25 | #zip -r deploy/chamgo-qt-osx.zip deploy/darwin 26 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "gopkg.in/yaml.v2" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "path/filepath" 9 | "runtime" 10 | ) 11 | 12 | var configfile = "config.yaml" 13 | 14 | type Config struct { 15 | Device []struct { 16 | Vendor string `yaml:"vendor"` 17 | Product string `yaml:"product"` 18 | Name string `yaml:"name"` 19 | Cdc string `yaml:"cdc"` 20 | CmdSet map[string]string `yaml:"cmdset"` 21 | Config struct { 22 | Slot struct { 23 | Selected int `yaml:"selected"` 24 | Offset int `yaml:"offset"` 25 | First int `yaml:"first"` 26 | Last int `yaml:"last"` 27 | } `yaml:"slot"` 28 | Serial struct { 29 | Baud int `yaml:"baud"` 30 | WaitForReceive int `yaml:"waitforreceive"` 31 | ConeectionTimeout int `yaml:"connectiontimeout"` 32 | Autoconnect bool `yaml:"autoconnect"` 33 | } `yaml:"serial"` 34 | MfkeyBin string `yaml:"mfkeybin"` 35 | } `yaml:"config"` 36 | } `yaml:"device"` 37 | Gui struct { 38 | Title string `yaml:"title"` 39 | } `yaml:"gui"` 40 | } 41 | 42 | func (c *Config) Load() *Config { 43 | cfgfile := Configpath() + configfile 44 | //log.Printf("Using configFile: %s\n", configfile) 45 | if len(cfgfile) > 0 { 46 | yamlFile, err := ioutil.ReadFile(cfgfile) 47 | if err != nil { 48 | log.Printf("error reading config (%s) err #%v ", cfgfile, err) 49 | os.Exit(2) 50 | } 51 | 52 | log.Println("loaded configfile: ", cfgfile) 53 | 54 | err = yaml.Unmarshal(yamlFile, c) 55 | if err != nil { 56 | log.Fatalf("Unmarshal: %v", err) 57 | return nil 58 | } 59 | return c 60 | } 61 | return nil 62 | } 63 | 64 | func (c *Config) Save() bool { 65 | cfgfile := Configpath() + configfile 66 | if len(cfgfile) > 0 { 67 | if data, err := yaml.Marshal(c); err != nil { 68 | log.Printf("error Marshall yaml (%s)\n", err) 69 | return false 70 | } else { 71 | err := ioutil.WriteFile(cfgfile, data, 0644) 72 | if err != nil { 73 | log.Fatal(err) 74 | } 75 | return true 76 | } 77 | } 78 | return false 79 | } 80 | 81 | func Configpath() string { 82 | 83 | dir, err := filepath.Abs(filepath.Dir(os.Args[0])) 84 | if err != nil { 85 | log.Printf("no executable-path found!?!\n%s\n", err) 86 | return "" 87 | } 88 | configpath := dir + string(os.PathSeparator) + runtime.GOOS + string(filepath.Separator) + "config" + string(filepath.Separator) 89 | if _, err := os.Stat(configpath + configfile); os.IsNotExist(err) { 90 | log.Printf("ConfigFile %s not found!\n", configpath+configfile) 91 | return "" 92 | } 93 | return configpath 94 | } 95 | 96 | func Apppath() string { 97 | 98 | appdir, err := filepath.Abs(filepath.Dir(os.Args[0])) 99 | if err != nil { 100 | log.Printf("no executable-path found!?!\n%s\n", err) 101 | return "" 102 | } 103 | return appdir 104 | } 105 | 106 | type DeviceActions struct { 107 | //config info 108 | GetModes string 109 | GetButtons string 110 | GetButtonsl string 111 | //slot info 112 | GetMode string 113 | GetUid string 114 | GetButton string 115 | GetButtonl string 116 | GetSize string 117 | //actions 118 | SelectSlot string 119 | SelectedSlot string 120 | ClearSlot string 121 | StartUpload string 122 | StartDownload string 123 | 124 | GetRssi string 125 | } 126 | 127 | func (d *DeviceActions) Load(commands map[string]string, device string) { 128 | switch device { 129 | 130 | case "Chameleon RevE-Rebooted": 131 | d.GetModes = commands["config"] 132 | d.GetButtons = commands["button"] 133 | 134 | d.GetMode = commands["config"] + "?" 135 | d.GetUid = commands["uid"] + "?" 136 | d.GetButton = commands["button"] + "?" 137 | d.GetButtonl = commands["buttonl"] + "?" 138 | d.GetSize = commands["memory"] + "?" 139 | 140 | d.SelectSlot = commands["setting"] + "=" 141 | d.SelectedSlot = commands["setting"] + "?" 142 | d.StartUpload = commands["upload"] 143 | d.StartDownload = commands["download"] 144 | d.ClearSlot = commands["clear"] 145 | 146 | d.GetRssi = commands["rssi"] + "?" 147 | 148 | case "Chameleon RevG": 149 | d.GetModes = commands["config"] + "=?" 150 | d.GetButtons = commands["button"] + "=?" 151 | 152 | d.GetMode = commands["config"] + "?" 153 | d.GetUid = commands["uid"] + "?" 154 | d.GetButton = commands["button"] + "?" 155 | d.GetButtonl = commands["buttonl"] + "?" 156 | d.GetSize = commands["memory"] + "?" 157 | 158 | d.SelectSlot = commands["setting"] + "=" 159 | d.SelectedSlot = commands["setting"] + "?" 160 | d.StartUpload = commands["upload"] 161 | d.StartDownload = commands["download"] 162 | d.ClearSlot = commands["clear"] 163 | 164 | d.GetRssi = commands["rssi"] + "?" 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /config/config.yaml: -------------------------------------------------------------------------------- 1 | device: 2 | - name: "Chameleon RevE-Rebooted" 3 | vendor: "03eb" 4 | product: "2044" 5 | cdc: "Atmel Corporation LUFA CDC" 6 | config: 7 | slot: 8 | selected: 4 9 | offset: 0 10 | first: 0 11 | last: 7 12 | serial: 13 | baud: 115200 14 | waitforreceive: 25 15 | connectiontimeout: 5 16 | autoconnect: false 17 | mfkeybin: mfkey32v2 18 | cmdset: # RevE-Rebooted 19 | version: VERSIONMY 20 | config: CONFIGMY 21 | uid: UIDMY 22 | upload: UPLOADMY 23 | download: DOWNLOADMY 24 | reset: RESETMY 25 | upgrade: UPGRADEMY 26 | memory: MEMSIZEMY 27 | uidsize: UIDSIZEMY 28 | button: BUTTONMY 29 | buttonl: BUTTON_LONGMY 30 | setting: SETTINGMY 31 | clear: CLEARMY 32 | help: HELPMY 33 | rssi: RSSIMY 34 | detection: DETECTIONMY 35 | - name: "Chameleon RevG" 36 | vendor: "16d0" 37 | product: "04b2" 38 | cdc: "KAOS Chameleon-Mini" 39 | config: 40 | slot: 41 | selected: 7 42 | offset: 1 43 | first: 1 44 | last: 8 45 | serial: 46 | baud: 115200 47 | waitforreceive: 100 48 | connectiontimeout: 10 49 | autoconnect: false 50 | cmdset: 51 | version: VERSION 52 | config: CONFIG 53 | uid: UID 54 | readonly: READONLY 55 | upload: UPLOAD 56 | download: DOWNLOAD 57 | reset: RESET 58 | upgrade: UPGRADE 59 | memory: MEMSIZE 60 | uidsize: UIDSIZE 61 | button: RBUTTON 62 | buttonl: RBUTTON_LONG 63 | rbutton: RBUTTON 64 | rbuttonl: RBUTTON_LONG 65 | lbutton: LBUTTON 66 | lbuttonl: LBUTTON_LONG 67 | setting: SETTING 68 | clear: CLEAR 69 | help: HELP 70 | rssi: RSSI 71 | ledgreen: LEDGREEN 72 | ledred: LEDRED 73 | logmode: LOGMODE 74 | logmem: LOGMEM 75 | logdownload: LOGDOWNLOAD 76 | logstore: LOGSTORE 77 | logclear: LOGCLEAR 78 | store: STORE 79 | recall: RECALL 80 | charging: CHARGING 81 | systick: SYSTICK 82 | sendraw: SEND_RAW 83 | send: SED 84 | getuid: GETUID 85 | dumpmfu: DUMP_MFU 86 | identify: IDENTIFY 87 | timeout: TIMEOUT 88 | threshold: THRESHOLD 89 | autocalibrate: AUTOCALIBRATE 90 | field: FIELD 91 | gui: 92 | title: "Chameleon Gui2" -------------------------------------------------------------------------------- /config/files.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "bufio" 5 | "log" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | ) 10 | 11 | func GetFilesInFolder(root string, ext string) []string { 12 | var files []string 13 | log.Printf("looking for files with extension %s in %s\n", root, ext) 14 | err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 15 | log.Printf("path: %s\n", path) 16 | if filepath.Ext(path) == ext { 17 | log.Printf("add %s\n", path) 18 | files = append(files, strings.Replace(path, root, "", 1)) 19 | } 20 | return nil 21 | }) 22 | if err != nil { 23 | panic(err) 24 | } 25 | for _, file := range files { 26 | log.Println(file) 27 | } 28 | return files 29 | } 30 | 31 | func ReadFileLines(path string) (res []string) { 32 | file, err := os.Open(path) 33 | if err != nil { 34 | log.Print(err) 35 | } 36 | defer file.Close() 37 | 38 | scanner := bufio.NewScanner(file) 39 | for scanner.Scan() { 40 | res = append(res, scanner.Text()) 41 | } 42 | 43 | if err := scanner.Err(); err != nil { 44 | log.Print(err) 45 | } 46 | return res 47 | } 48 | -------------------------------------------------------------------------------- /config/map.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "gopkg.in/yaml.v2" 5 | "log" 6 | "io/ioutil" 7 | "path/filepath" 8 | "runtime" 9 | ) 10 | 11 | const MYFAIRE_CLASSIC_1K_4Byte_UID = 0x0400 12 | 13 | type TagMap struct { 14 | Name string `yaml:"name"` 15 | Tagtype string `yaml:"tagtype"` 16 | Mappings []ByteMap `yaml:"mappings"` 17 | } 18 | type ByteMap struct { 19 | Start int `yaml:"start"` 20 | End int `yaml:"end"` 21 | MapBytes []MapByte `yaml:"mapbytes"` 22 | MapFuncs []MapFunc `yaml:"mapfuncs"` 23 | } 24 | type MapByte struct { 25 | Pos int `yaml:"pos"` 26 | Color string `yaml:"color"` 27 | Tooltip string `yaml:"tooltip"` 28 | } 29 | type MapFunc struct{ 30 | Name string `yaml:"name"` 31 | ExpectResult bool `yaml:"expectresult"` 32 | Result []string `yaml:"result"` 33 | } 34 | 35 | var DefaultMap = TagMap{ 36 | Name: "Mifare Classic 1K 4Byte UID", 37 | Tagtype: "0400", 38 | Mappings: []ByteMap{ 39 | { 40 | Start: 0, 41 | End:3, 42 | MapBytes: []MapByte{ 43 | { 44 | Pos: 0, 45 | Color: "yellow", 46 | Tooltip: "UID0", 47 | }, 48 | { 49 | Pos: 1, 50 | Color: "yellow", 51 | Tooltip: "UID1", 52 | }, 53 | { 54 | Pos: 2, 55 | Color: "yellow", 56 | Tooltip: "UID2", 57 | }, 58 | { 59 | Pos: 3, 60 | Color: "yellow", 61 | Tooltip: "UID3", 62 | }, 63 | }, 64 | }, 65 | { 66 | Start: 4, 67 | End:4, 68 | MapBytes: []MapByte{ 69 | { 70 | Pos: 0, 71 | Color: "yellow", 72 | Tooltip: "BCC (UID0..UID3)", 73 | }, 74 | }, 75 | MapFuncs: []MapFunc{ 76 | { 77 | Name: "bcc", 78 | ExpectResult: true, 79 | Result: []string{"fc"}, 80 | }, 81 | }, 82 | }, 83 | }, 84 | } 85 | 86 | func (m *TagMap) Save(f string) { 87 | tagmap := Apppath()+string(filepath.Separator)+runtime.GOOS+string(filepath.Separator)+"maps"+string(filepath.Separator)+f 88 | if len(m.Mappings) > 0 { 89 | if data, err := yaml.Marshal(m); err != nil { 90 | log.Printf("error Marshall yaml (%s)\n", err) 91 | } else { 92 | err := ioutil.WriteFile(tagmap, data, 0644) 93 | if err != nil { 94 | log.Print(err) 95 | } 96 | } 97 | } 98 | } 99 | 100 | func (c *TagMap) Load(f string) { 101 | tagmap := Apppath()+string(filepath.Separator)+runtime.GOOS+string(filepath.Separator)+"maps"+string(filepath.Separator)+f 102 | //log.Printf("Using configFile: %s\n", configfile) 103 | if len(tagmap) > 0 { 104 | yamlFile, err := ioutil.ReadFile(tagmap) 105 | if err != nil { 106 | log.Printf("error reading config (%s) err #%v ", tagmap, err) 107 | return 108 | } 109 | log.Println("loaded TagMap: ", c.Name) 110 | 111 | err = yaml.Unmarshal(yamlFile, c) 112 | if err != nil { 113 | log.Print("Unmarshal: %v", err) 114 | } 115 | } 116 | } -------------------------------------------------------------------------------- /crc16/crc.go: -------------------------------------------------------------------------------- 1 | // Package crc16 implements the 16-bit cyclic redundancy check, or CRC-16, checksum. 2 | // 3 | // It provides parameters for the majority of well-known CRC-16 algorithms. 4 | package crc16 5 | 6 | import ( 7 | "encoding/hex" 8 | "fmt" 9 | ) 10 | 11 | // Params represents parameters of CRC-16 algorithms. 12 | // More information about algorithms parametrization and parameter descriptions 13 | // can be found here - http://www.zlib.net/crc_v3.txt 14 | type Params struct { 15 | Poly uint16 16 | Init uint16 17 | RefIn bool 18 | RefOut bool 19 | XorOut uint16 20 | Check uint16 21 | Name string 22 | } 23 | 24 | // Predefined CRC-16 algorithms. 25 | var ( 26 | CRC16_CRC_A = Params{0x1021, 0xC6C6, true, true, 0x0000, 0xBF05, "CRC-16/CRC-A"} 27 | ) 28 | 29 | // Table is a 256-word table representing polinomial and algorithm settings for efficient processing. 30 | type Table struct { 31 | params Params 32 | data [256]uint16 33 | } 34 | 35 | // MakeTable returns the Table constructed from the specified algorithm. 36 | func MakeTable(params Params) *Table { 37 | table := new(Table) 38 | table.params = params 39 | for n := 0; n < 256; n++ { 40 | crc := uint16(n) << 8 41 | for i := 0; i < 8; i++ { 42 | bit := (crc & 0x8000) != 0 43 | crc <<= 1 44 | if bit { 45 | crc ^= params.Poly 46 | } 47 | } 48 | table.data[n] = crc 49 | } 50 | return table 51 | } 52 | 53 | // Init returns the initial value for CRC register corresponding to the specified algorithm. 54 | func Init(table *Table) uint16 { 55 | return table.params.Init 56 | } 57 | 58 | // Update returns the result of adding the bytes in data to the crc. 59 | func Update(crc uint16, data []byte, table *Table) uint16 { 60 | for _, d := range data { 61 | if table.params.RefIn { 62 | d = ReverseByte(d) 63 | } 64 | crc = crc<<8 ^ table.data[byte(crc>>8)^d] 65 | } 66 | return crc 67 | } 68 | 69 | // Complete returns the result of CRC calculation and post-calculation processing of the crc. 70 | func Complete(crc uint16, table *Table) uint16 { 71 | if table.params.RefOut { 72 | return ReverseUint16(crc) ^ table.params.XorOut 73 | } 74 | return crc ^ table.params.XorOut 75 | } 76 | 77 | // Checksum returns CRC checksum of data usign scpecified algorithm represented by the Table. 78 | func Checksum(data []byte, table *Table) uint16 { 79 | crc := Init(table) 80 | crc = Update(crc, data, table) 81 | return Complete(crc, table) 82 | } 83 | 84 | func ReverseByte(val byte) byte { 85 | var rval byte = 0 86 | for i := uint(0); i < 8; i++ { 87 | if val&(1<> i 89 | } 90 | } 91 | return rval 92 | } 93 | 94 | func ReverseUint16(val uint16) uint16 { 95 | var rval uint16 = 0 96 | for i := uint(0); i < 16; i++ { 97 | if val&(uint16(1)<> i 99 | } 100 | } 101 | return rval 102 | } 103 | 104 | //calculate BCC of a Mifare Classic 105 | func GetBCC(input []byte) (bcc byte) { 106 | bcc = byte(0x0) 107 | 108 | if len(input) > 0 { 109 | for i := 0; i < len(input); i++ { 110 | bcc ^= input[i] 111 | } 112 | } 113 | return bcc 114 | } 115 | 116 | func Split2Hex(s string) []byte { 117 | res, err := hex.DecodeString(s) 118 | if err != nil { 119 | return nil 120 | } 121 | return res 122 | } 123 | 124 | func GetCRCA(hexstr string) string { 125 | strhex := Split2Hex(hexstr) 126 | t := MakeTable(CRC16_CRC_A) 127 | checksum5 := Checksum(strhex, t) 128 | var h, l uint8 = uint8(checksum5 >> 8), uint8(checksum5 & 0xff) 129 | return fmt.Sprintf("%02X%02X", l, h) 130 | } 131 | -------------------------------------------------------------------------------- /dataTab.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "github.com/WolfgangMau/chamgo-qt/config" 6 | "github.com/WolfgangMau/chamgo-qt/crc16" 7 | "github.com/WolfgangMau/chamgo-qt/eml2dump" 8 | "github.com/WolfgangMau/chamgo-qt/xmodem" 9 | "github.com/therecipe/qt/core" 10 | "github.com/therecipe/qt/widgets" 11 | "log" 12 | "math" 13 | "path/filepath" 14 | "runtime" 15 | "strconv" 16 | "strings" 17 | ) 18 | 19 | type QTbytes struct { 20 | LineEdits []*widgets.QLineEdit 21 | Labels []*widgets.QLabel 22 | } 23 | 24 | var ScrollLock int 25 | var RadioTag string 26 | 27 | func dataTab() *widgets.QWidget { 28 | tablayout := widgets.NewQHBoxLayout() 29 | dataTabPage := widgets.NewQWidget(nil, 0) 30 | dump2emulBtn := widgets.NewQPushButton2("Dump > Emul", nil) 31 | dump2emulBtn.SetToolTip("convert Binary-File to ASCII-File") 32 | dump2emulBtn.SetFixedWidth(120) 33 | dump2emulBtn.ConnectClicked(func(checked bool) { 34 | 35 | fromFileSelect := widgets.NewQFileDialog(nil, 0) 36 | fromFilename := fromFileSelect.GetOpenFileName(nil, "open File", "", "Bin Files (*.dump *.mfd *.bin);;All Files (*.*)", "", fromFileSelect.Options()) 37 | if fromFilename == "" { 38 | log.Println("no file selected") 39 | return 40 | } 41 | 42 | toFileSelect := widgets.NewQFileDialog(nil, 0) 43 | toFilename := toFileSelect.GetSaveFileName(nil, "save Data to File", "", "", "", toFileSelect.Options()) 44 | if toFilename == "" { 45 | log.Println("no file seleted") 46 | return 47 | } 48 | 49 | inData, err := eml2dump.File2Bytes(fromFilename) 50 | if err != nil { 51 | log.Print(err) 52 | return 53 | } 54 | 55 | if !eml2dump.Bytes2Emul(toFilename, inData) { 56 | log.Print("faiöled to write data to ", toFilename) 57 | return 58 | } 59 | }) 60 | 61 | emul2dumpBtn := widgets.NewQPushButton2("Emul > Dump", nil) 62 | emul2dumpBtn.SetToolTip("convert ASCII-File to Binary-File") 63 | emul2dumpBtn.SetFixedWidth(120) 64 | emul2dumpBtn.ConnectClicked(func(checked bool) { 65 | 66 | fromFileSelect := widgets.NewQFileDialog(nil, 0) 67 | fromFilename := fromFileSelect.GetOpenFileName(nil, "open File", "", "Emulator Files (*.eml *.emul *.txt);;All Files (*.*)", "", fromFileSelect.Options()) 68 | if fromFilename == "" { 69 | log.Println("no file selected") 70 | return 71 | } 72 | 73 | toFileSelect := widgets.NewQFileDialog(nil, 0) 74 | toFilename := toFileSelect.GetSaveFileName(nil, "save Data to File", "", "", "", toFileSelect.Options()) 75 | if toFilename == "" { 76 | log.Println("no file seleted") 77 | return 78 | } 79 | 80 | filedata, err := eml2dump.File2Bytes(fromFilename) 81 | if err != nil { 82 | log.Print(err) 83 | return 84 | } 85 | bindata, err := hex.DecodeString(strings.Replace(strings.Replace(string(filedata), "\n", "", -1), "\r", "", -1)) 86 | if err != nil { 87 | log.Print(err) 88 | return 89 | } 90 | eml2dump.Bytes2File(toFilename, bindata) 91 | }) 92 | 93 | loadTagABtn := widgets.NewQPushButton2("Dump > Tag A", nil) 94 | loadTagABtn.SetToolTip("load Data from Binary-File to Tag A") 95 | loadTagABtn.SetFixedWidth(120) 96 | loadTagABtn.ConnectClicked(func(checked bool) { 97 | fromFileSelect := widgets.NewQFileDialog(nil, 0) 98 | fromFilename := fromFileSelect.GetOpenFileName(nil, "open File", "", "Bin Files (*.dump *.mfd *.bin);;All Files (*.*)", "", fromFileSelect.Options()) 99 | if fromFilename == "" { 100 | log.Println("no file selected") 101 | return 102 | } 103 | TagA.FillFromFile(fromFilename) 104 | }) 105 | 106 | loadTagBBtn := widgets.NewQPushButton2("Dump > Tag B", nil) 107 | loadTagBBtn.SetToolTip("load Data from Binary-File to Tag A+B") 108 | loadTagBBtn.SetFixedWidth(120) 109 | loadTagBBtn.ConnectClicked(func(checked bool) { 110 | fromFileSelect := widgets.NewQFileDialog(nil, 0) 111 | fromFilename := fromFileSelect.GetOpenFileName(nil, "open File", "", "Bin Files (*.dump *.mfd *.bin);;All Files (*.*)", "", fromFileSelect.Options()) 112 | if fromFilename == "" { 113 | log.Println("no file selected") 114 | return 115 | } 116 | TagB.FillFromFile(fromFilename) 117 | }) 118 | 119 | diffTagBtn := widgets.NewQPushButton2("Diff Tags", nil) 120 | diffTagBtn.SetToolTip("show differences between Tag A & Tag B") 121 | diffTagBtn.SetFixedWidth(120) 122 | diffTagBtn.ConnectClicked(func(checked bool) { 123 | DiffTags(TagA, TagB) 124 | }) 125 | 126 | mapfiles := config.GetFilesInFolder(config.Apppath()+string(filepath.Separator)+runtime.GOOS+string(filepath.Separator)+"maps"+string(filepath.Separator), ".map") 127 | mapSelect := widgets.NewQComboBox(nil) 128 | mapSelect.SetFixedWidth(120) 129 | mapSelect.AddItems([]string{"mappings"}) 130 | if len(mapfiles) > 0 { 131 | mapSelect.AddItems(mapfiles) 132 | } else { 133 | log.Print("creating dummy-map dummy.map") 134 | temp := config.DefaultMap 135 | temp.Save("dummy.map") 136 | mapSelect.AddItems([]string{"dummy.map"}) 137 | } 138 | mapSelect.ConnectCurrentIndexChanged(func(index int) { 139 | if index > 0 { 140 | tagmap := config.TagMap{} 141 | tagmap.Load(mapSelect.CurrentText()) 142 | TagA.Map(tagmap) 143 | TagB.Map(tagmap) 144 | mapSelect.SetCurrentIndex(0) 145 | mapSelect.Repaint() 146 | } 147 | }) 148 | 149 | var slotItems []string 150 | slotSelect := widgets.NewQComboBox(nil) 151 | slotSelect.SetToolTip("load Data from Slot to Tag A or B") 152 | slotSelect.SetFixedWidth(120) 153 | slotSelect.ConnectCurrentIndexChanged(func(index int) { 154 | if Connected && index > 0 { 155 | data := getSlotBytes(index - 1) 156 | if len(data) > 0 { 157 | if RadioTag == "A" { 158 | TagA.FillFromBytes(data) 159 | } else { 160 | TagB.FillFromBytes(data) 161 | } 162 | slotSelect.SetCurrentIndex(0) 163 | } 164 | } 165 | }) 166 | slotSelect.AddItems([]string{"Slot > Tag"}) 167 | 168 | for i := Cfg.Device[SelectedDeviceId].Config.Slot.First; i <= Cfg.Device[SelectedDeviceId].Config.Slot.Last; i++ { 169 | slotItems = append(slotItems, "Slot "+strconv.Itoa(i)) 170 | } 171 | slotSelect.AddItems(slotItems) 172 | 173 | slot2Select := widgets.NewQComboBox(nil) 174 | slot2Select.SetToolTip("load Data from Slot to Tag A or B") 175 | slot2Select.SetFixedWidth(120) 176 | slot2Select.ConnectCurrentIndexChanged(func(index int) { 177 | if Connected && index > 0 { 178 | var data []byte 179 | var tag QTbytes 180 | switch RadioTag { 181 | case "A": 182 | tag = TagA 183 | case "B": 184 | tag = TagB 185 | } 186 | for _, le := range tag.LineEdits { 187 | lbyte, err := hex.DecodeString(le.Text()) 188 | 189 | if err != nil { 190 | log.Print(err) 191 | } else { 192 | if len(lbyte) == 1 { 193 | data = append(data, lbyte[0]) 194 | } 195 | } 196 | } 197 | if len(data) > 0 { 198 | if RadioTag == "A" { 199 | TagA.Tag2Slot(index-1, data) 200 | } else { 201 | TagB.Tag2Slot(index-1, data) 202 | } 203 | slot2Select.SetCurrentIndex(0) 204 | } 205 | } 206 | }) 207 | slot2Select.AddItems([]string{"Tag > Slot"}) 208 | slot2Select.AddItems(slotItems) 209 | 210 | lockScrollChk := widgets.NewQCheckBox2("ScrollLock", nil) 211 | lockScrollChk.SetToolTip("Scroll together or separately") 212 | lockScrollChk.SetChecked(true) 213 | ScrollLock = 2 214 | lockScrollChk.ConnectStateChanged(func(state int) { 215 | 216 | ScrollLock = state 217 | log.Printf("ScrollLock: %d", ScrollLock) 218 | 219 | }) 220 | 221 | //left menuy 222 | 223 | dataTabLayout := widgets.NewQGridLayout(nil) 224 | dataTabLayout.SetAlign(core.Qt__AlignTop) 225 | 226 | //slot action 227 | slotGroup := widgets.NewQGroupBox2("Slot Import / Export", nil) 228 | slotlayout := widgets.NewQVBoxLayout() 229 | radioLayout := widgets.NewQHBoxLayout() 230 | tagARadio := widgets.NewQRadioButton2("Tag A", slotGroup) 231 | tagARadio.ConnectClicked(func(checked bool) { 232 | if checked { 233 | RadioTag = "A" 234 | } else { 235 | RadioTag = "B" 236 | } 237 | }) 238 | tagARadio.SetChecked(true) 239 | RadioTag = "A" 240 | tagBRadio := widgets.NewQRadioButton2("Tag B", slotGroup) 241 | tagBRadio.ConnectClicked(func(checked bool) { 242 | if checked { 243 | RadioTag = "B" 244 | } else { 245 | RadioTag = "A" 246 | } 247 | }) 248 | radioLayout.AddWidget(tagARadio, 0, core.Qt__AlignCenter) 249 | radioLayout.AddWidget(tagBRadio, 0, core.Qt__AlignCenter) 250 | slotlayout.AddLayout(radioLayout, 0) 251 | slotlayout.AddWidget(slotSelect, 0, core.Qt__AlignCenter) 252 | slotlayout.AddWidget(slot2Select, 0, core.Qt__AlignCenter) 253 | slotGroup.SetFixedWidth(155) 254 | slotGroup.SetLayout(slotlayout) 255 | 256 | //File based action 257 | fileGroup := widgets.NewQGroupBox2("File Import / Export", nil) 258 | fileLayout := widgets.NewQVBoxLayout() 259 | fileLayout.AddWidget(dump2emulBtn, 0, core.Qt__AlignCenter) 260 | fileLayout.AddWidget(emul2dumpBtn, 0, core.Qt__AlignCenter) 261 | fileLayout.AddWidget(loadTagABtn, 0, core.Qt__AlignCenter) 262 | fileLayout.AddWidget(loadTagBBtn, 0, core.Qt__AlignCenter) 263 | fileGroup.SetFixedWidth(155) 264 | fileGroup.SetLayout(fileLayout) 265 | 266 | //Diff/Map based actions 267 | diffGroup := widgets.NewQGroupBox2("Diff / Map", nil) 268 | difflayout := widgets.NewQVBoxLayout() 269 | difflayout.AddWidget(diffTagBtn, 0, core.Qt__AlignCenter) 270 | difflayout.AddWidget(mapSelect, 0, core.Qt__AlignCenter) 271 | difflayout.AddWidget(lockScrollChk, 0, core.Qt__AlignCenter) 272 | diffGroup.SetFixedWidth(155) 273 | diffGroup.SetLayout(difflayout) 274 | 275 | dataTabLayout.AddWidget(fileGroup, 0, 0, core.Qt__AlignCenter) 276 | dataTabLayout.AddWidget(slotGroup, 1, 0, core.Qt__AlignCenter) 277 | dataTabLayout.AddWidget(diffGroup, 2, 0, core.Qt__AlignCenter) 278 | tablayout.AddLayout(dataTabLayout, 0) 279 | 280 | tagALayout := widgets.NewQVBoxLayout() 281 | scrollerA := TagA.Create(true) 282 | tagAInfo := widgets.NewQLabel2("Tag A", nil, 0) 283 | tagALayout.AddWidget(tagAInfo, 0, core.Qt__AlignCenter) 284 | tagALayout.AddWidget(scrollerA, 0, core.Qt__AlignLeft) 285 | tablayout.AddLayout(tagALayout, 1) 286 | 287 | tagBLayout := widgets.NewQVBoxLayout() 288 | scrollerB := TagB.Create(false) 289 | tagBInfo := widgets.NewQLabel2("Tag B", nil, 0) 290 | tagBLayout.AddWidget(tagBInfo, 0, core.Qt__AlignCenter) 291 | tagBLayout.AddWidget(scrollerB, 0, core.Qt__AlignLeft) 292 | tablayout.AddLayout(tagBLayout, 0) 293 | 294 | scrollerA.VerticalScrollBar().ConnectValueChanged(func(positionA int) { 295 | if ScrollLock == 2 { 296 | scrollerB.VerticalScrollBar().SetValue(positionA) 297 | } 298 | }) 299 | scrollerB.VerticalScrollBar().ConnectValueChanged(func(positionB int) { 300 | if ScrollLock == 2 { 301 | scrollerA.VerticalScrollBar().SetValue(positionB) 302 | } 303 | }) 304 | dataTabPage.SetLayout(tablayout) 305 | return dataTabPage 306 | } 307 | 308 | func (QTbytesGrid *QTbytes) Create(labelIt bool) *widgets.QScrollArea { 309 | wrapper := widgets.NewQWidget(nil, 0) 310 | scroller := widgets.NewQScrollArea(nil) 311 | scroller.SetWidgetResizable(true) 312 | if labelIt { 313 | scroller.SetFixedWidth(435) 314 | } else { 315 | scroller.SetFixedWidth(380) 316 | } 317 | scroller.SetWidget(wrapper) 318 | sl := widgets.NewQGridLayout(scroller) 319 | sl.SetSpacing(2) 320 | sl.SetAlign(core.Qt__AlignLeft) 321 | 322 | startRow := 0 323 | startcell := 0 324 | byteCount := 0 325 | blockCount := 0 326 | header := false 327 | for i := 0; i <= 64; i++ { 328 | for i2 := 0; i2 <= 15; i2++ { 329 | if !header { 330 | QTbytesGrid.Labels = append(QTbytesGrid.Labels, widgets.NewQLabel(nil, 0)) 331 | QTbytesGrid.Labels[i2].SetText(strconv.Itoa(i2)) 332 | sl.AddWidget(QTbytesGrid.Labels[i2], startRow+i, startcell+i2+1, core.Qt__AlignHCenter) 333 | 334 | } else { 335 | if labelIt && i2 == 0 { 336 | sector := int(math.Floor(float64(blockCount-1) / 4)) 337 | blockLabel := widgets.NewQLabel2("Block "+strconv.Itoa(blockCount-1), nil, 0) 338 | blockLabel.SetToolTip("Sector " + strconv.Itoa(sector)) 339 | sl.AddWidget(blockLabel, startRow+i, 0, core.Qt__AlignLeft) 340 | } 341 | QTbytesGrid.LineEdits = append(QTbytesGrid.LineEdits, widgets.NewQLineEdit(nil)) 342 | QTbytesGrid.LineEdits[byteCount].SetToolTip("Byte #" + strconv.Itoa(byteCount)) 343 | QTbytesGrid.LineEdits[byteCount].SetMaxLength(2) 344 | QTbytesGrid.LineEdits[byteCount].SetFixedWidth(20) 345 | QTbytesGrid.LineEdits[byteCount].SetAlignment(core.Qt__AlignHCenter) 346 | 347 | sl.AddWidget(QTbytesGrid.LineEdits[byteCount], startRow+i, startcell+i2+1, core.Qt__AlignLeft) 348 | byteCount++ 349 | } 350 | } 351 | header = true 352 | blockCount++ 353 | } 354 | 355 | wrapper.SetLayout(sl) 356 | return scroller 357 | } 358 | 359 | func (QTbytesGrid *QTbytes) FillFromFile(filename string) { 360 | data, _ := eml2dump.File2Bytes(filename) 361 | if len(QTbytesGrid.LineEdits) != len(data) { 362 | log.Printf("data-Len missmamatch grid: %d - file: %d", len(QTbytesGrid.LineEdits), len(data)) 363 | return 364 | } 365 | for i, b := range data { 366 | QTbytesGrid.LineEdits[i].SetText(hex.EncodeToString([]byte{b})) 367 | } 368 | } 369 | 370 | func (QTbytesGrid *QTbytes) FillFromBytes(data []byte) { 371 | if len(QTbytesGrid.LineEdits) != len(data) { 372 | log.Printf("data-Len missmamatch grid: %d - data: %d", len(QTbytesGrid.LineEdits), len(data)) 373 | return 374 | } 375 | for i, b := range data { 376 | QTbytesGrid.LineEdits[i].SetText(hex.EncodeToString([]byte{b})) 377 | } 378 | } 379 | 380 | func (QTbytesGrid *QTbytes) Tag2Slot(slotnumber int, data []byte) { 381 | hardwareSlot := slotnumber + Cfg.Device[SelectedDeviceId].Config.Slot.Offset 382 | sendSerialCmd(DeviceActions.SelectSlot + strconv.Itoa(hardwareSlot)) 383 | p := Bytes2Packets(data) 384 | //set chameleon into receiver-mode 385 | sendSerialCmd(DeviceActions.StartUpload) 386 | if SerialResponse.Code == 110 { 387 | //start uploading packets 388 | xmodem.Send(SerialPort, p) 389 | } 390 | } 391 | 392 | func (QTbytesGrid *QTbytes) SetColor(index int, color string, selcolor string) { 393 | style := "background: " + rgbColorString(color) + " selection-background-color: " + rgbColorString(selcolor) 394 | QTbytesGrid.LineEdits[index].SetStyleSheet(style) 395 | QTbytesGrid.LineEdits[index].Repaint() 396 | } 397 | 398 | func (QTbytesGrid *QTbytes) SetTooltip(index int, tip string) { 399 | QTbytesGrid.LineEdits[index].SetToolTip(tip) 400 | QTbytesGrid.LineEdits[index].Repaint() 401 | } 402 | 403 | func rgbColorString(color string) (res string) { 404 | switch color { 405 | case "red": 406 | res = "rgb(255, 0, 0);" 407 | case "lightred": 408 | res = "rgb(255, 71, 26);" 409 | 410 | case "green": 411 | res = "rgb(51, 153, 51);" 412 | case "lightgreen": 413 | res = "rgb(0, 204, 0);" 414 | case "limegreen": 415 | res = "rgb(0, 255, 0);" 416 | 417 | case "blue": 418 | res = "rgb(0, 102, 255);" 419 | case "lightblue": 420 | res = "rgb(51, 204, 255);" 421 | 422 | case "yellow": 423 | res = "rgb(255, 255, 0);" 424 | 425 | case "purple": 426 | res = "rgb(153, 51, 255);" 427 | 428 | case "magenta": 429 | res = "rgb(255, 0, 255);" 430 | 431 | case "grey": 432 | res = "rgb(179, 179, 179);" 433 | 434 | case "defaultsel": 435 | res = "rgb(51, 204, 255);" 436 | 437 | default: 438 | res = "rgb(255,255,255);" 439 | } 440 | return res 441 | } 442 | 443 | func DiffTags(tagA QTbytes, tagB QTbytes) { 444 | for i, le := range tagA.LineEdits { 445 | if strings.ToLower(le.Text()) != strings.ToLower(tagB.LineEdits[i].Text()) { 446 | tagA.SetColor(i, "lightgreen", "defaultsel") 447 | tagB.SetColor(i, "lightred", "defaultsel") 448 | } else { 449 | tagA.SetColor(i, "default", "defaultsel") 450 | tagB.SetColor(i, "default", "defaultsel") 451 | } 452 | } 453 | } 454 | 455 | func (tag *QTbytes) Map(tm config.TagMap) { 456 | if len(tm.Mappings) > 0 { 457 | for _, m := range tm.Mappings { 458 | if len(m.MapBytes) > 0 { 459 | if len(m.MapFuncs) == 0 { 460 | for _, mb := range m.MapBytes { 461 | tag.SetColor(mb.Pos, mb.Color, "defaultsel") 462 | tag.SetTooltip(mb.Pos, mb.Tooltip) 463 | } 464 | } else { 465 | words := strings.Split(m.MapFuncs[0].Name, " ") 466 | if len(words) == 3 { 467 | switch strings.ToLower(words[0]) { 468 | case "each": 469 | switch strings.ToLower(words[1]) { 470 | case "sectorblock": 471 | targetBlock, _ := strconv.Atoi(words[2]) 472 | sectorBlock := 0 473 | sector := 0 474 | blockByte := 0 475 | for i := range tag.LineEdits { 476 | if blockByte == 16 { 477 | sectorBlock++ 478 | blockByte = 0 479 | if sectorBlock == 4 { 480 | sector++ 481 | sectorBlock = 0 482 | } 483 | } 484 | if sectorBlock == targetBlock { 485 | if blockByte >= m.Start && blockByte <= m.End { 486 | tag.SetColor(i, m.MapBytes[0].Color, "defaultsel") 487 | tag.SetTooltip(i, "Sector: "+strconv.Itoa(sector)+" - sectorBlock: "+strconv.Itoa(sectorBlock)+" blockByte: "+strconv.Itoa(i)+"\n"+m.MapBytes[0].Tooltip) 488 | } 489 | } 490 | blockByte++ 491 | } 492 | } 493 | case "check": 494 | switch strings.ToLower(words[1]) { 495 | case "bcc": 496 | if tag.CalcBCC(4) == tag.LineEdits[4].Text() { 497 | tag.SetColor(4, words[2], "defaultsel") 498 | tag.SetTooltip(4, "BCC OK") 499 | } else { 500 | tag.SetColor(4, m.MapBytes[0].Color, "defaultsel") 501 | tag.SetTooltip(4, "BCC != "+tag.CalcBCC(4)) 502 | } 503 | } 504 | } 505 | } 506 | } 507 | } 508 | } 509 | } 510 | } 511 | 512 | func (tag *QTbytes) GetId(size int) string { 513 | var res string 514 | for i := 0; i < size; i++ { 515 | res += tag.LineEdits[i].Text() 516 | } 517 | return res 518 | } 519 | 520 | func (tag *QTbytes) CalcBCC(size int) string { 521 | id := tag.GetId(size) 522 | b, _ := hex.DecodeString(id) 523 | bcc2 := hex.EncodeToString([]byte{crc16.GetBCC(b)}) 524 | return bcc2 525 | } 526 | -------------------------------------------------------------------------------- /eml2dump/eml2dump.go: -------------------------------------------------------------------------------- 1 | package eml2dump 2 | 3 | import ( 4 | "os" 5 | "log" 6 | "encoding/hex" 7 | "io/ioutil" 8 | ) 9 | 10 | func Bytes2File(filename string, filebytes []byte) (err error) { 11 | //open file for write 12 | outFile, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) 13 | if err != nil { 14 | log.Println(err) 15 | return err 16 | } 17 | defer outFile.Close() 18 | 19 | //write data to file 20 | if _, err = outFile.Write(filebytes); err != nil { 21 | log.Println(err) 22 | return err 23 | } 24 | log.Printf("wrote %d bytes to file: %s", len(filebytes),filename) 25 | return nil 26 | } 27 | 28 | func File2Bytes(filename string) (filedata []byte, err error) { 29 | 30 | //does the fikle exists? 31 | if _, err := os.Stat(filename); os.IsNotExist(err) { 32 | log.Println(err) 33 | return filedata, err 34 | } 35 | 36 | //read filedata 37 | filedata,_ = ioutil.ReadFile(filename) 38 | log.Printf("read %d bytes from file: %s", len(filedata),filename) 39 | return filedata, err 40 | } 41 | 42 | func Bytes2Emul(filename string, data []byte) bool { 43 | encoded := hex.EncodeToString(data) 44 | //open file for write 45 | outFile, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) 46 | if err != nil { 47 | log.Println(err) 48 | return false 49 | } 50 | defer outFile.Close() 51 | //write 32char-lines 52 | for i:=1;i= 3 { 167 | shortInfo = toks[0] + " " + toks[1] 168 | } else { 169 | shortInfo = longInfo 170 | } 171 | return 172 | } 173 | 174 | func prepareResponse(res string) { 175 | var result []string 176 | res = strings.Replace(res, "\n", "#", -1) 177 | res = strings.Replace(res, "\r", "#", -1) 178 | res = strings.Replace(res, "##", "#", -1) 179 | 180 | if !strings.Contains(res, ":") { 181 | log.Println("no response given") 182 | SerialPort.ResetInputBuffer() 183 | return 184 | } 185 | temp := strings.Split(res, ":") 186 | if len(temp[1]) >= 2 { 187 | result = append(result, temp[0]) 188 | SerialResponse.Code, _ = strconv.Atoi(temp[0]) 189 | temp := strings.Split(temp[1], "#") 190 | if len(temp) > 0 { 191 | for i, s := range temp { 192 | switch i { 193 | case 0: 194 | SerialResponse.String = s 195 | case 1: 196 | SerialResponse.Payload = s 197 | } 198 | if s != "" { 199 | result = append(result, s) 200 | } 201 | } 202 | } 203 | } 204 | } 205 | 206 | func SerialSendOnly(cmd string) { 207 | _, err := SerialPort.Write([]byte(strings.ToUpper(cmd) + "\r\n")) 208 | if err != nil { 209 | log.Println(err) 210 | } 211 | time.Sleep(time.Millisecond * time.Duration(Cfg.Device[SelectedDeviceId].Config.Serial.WaitForReceive)) 212 | return 213 | } 214 | 215 | func GetSpecificBytes(size int) []byte { 216 | c1 := make(chan []byte, size) 217 | go func() { 218 | 219 | buff := make([]byte, size) 220 | c := 0 221 | for c < size { 222 | n := 0 223 | n, err := SerialPort.Read(buff) 224 | if err != nil { 225 | log.Println(err) 226 | } 227 | c = c + n 228 | } 229 | c1 <- buff 230 | }() 231 | select { 232 | case buff := <-c1: 233 | return buff 234 | case <-time.After(time.Second * time.Duration(Cfg.Device[SelectedDeviceId].Config.Serial.ConeectionTimeout)): 235 | log.Println("GetSpecificBytes Timeout") 236 | } 237 | 238 | return nil 239 | } 240 | -------------------------------------------------------------------------------- /slotFunctions.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "fmt" 7 | "github.com/WolfgangMau/chamgo-qt/nonces" 8 | "github.com/WolfgangMau/chamgo-qt/xmodem" 9 | "github.com/therecipe/qt/widgets" 10 | "io/ioutil" 11 | "log" 12 | "os" 13 | "os/exec" 14 | "strconv" 15 | "strings" 16 | "time" 17 | ) 18 | 19 | var myTime time.Time 20 | var GetUsbListTicker *time.Ticker 21 | 22 | //noinspection GoPrintFunctions 23 | func buttonClicked(btn int) { 24 | 25 | switch ActionButtons[btn] { 26 | 27 | case "Select All": 28 | selectAllSlots(true) 29 | if populated { 30 | } 31 | 32 | case "Select None": 33 | selectAllSlots(false) 34 | if populated { 35 | } 36 | 37 | case "Apply": 38 | applySlot() 39 | 40 | case "Clear": 41 | clearSlot() 42 | 43 | case "Refresh": 44 | refreshSlot() 45 | 46 | case "Set Active": 47 | activateSlots() 48 | 49 | case "mfkey32": 50 | mfkey32Slots() 51 | 52 | case "Upload": 53 | uploadSlots() 54 | 55 | case "Download": 56 | downloadSlots() 57 | 58 | case "RSSI": 59 | getRssi() 60 | 61 | default: 62 | log.Printf("clicked on Button: %s\n", ActionButtons[btn]) 63 | } 64 | } 65 | 66 | func getRssi() { 67 | if Connected { 68 | sendSerialCmd(DeviceActions.GetRssi) 69 | RssiVal.SetText(SerialResponse.Payload) 70 | RssiVal.Repaint() 71 | } 72 | } 73 | 74 | //noinspection GoPrintFunctions 75 | func slotChecked(slot, state int) { 76 | log.Printf(" Checked %d - state: %d\n", slot, state) 77 | if state == 2 && Connected { 78 | sendSerialCmd(DeviceActions.SelectSlot + strconv.Itoa(slot+Cfg.Device[SelectedDeviceId].Config.Slot.Offset)) 79 | } 80 | Slots[slot].slot.Repaint() 81 | } 82 | 83 | func selectAllSlots(b bool) { 84 | for _, s := range Slots { 85 | s.slot.SetChecked(b) 86 | s.slot.Repaint() 87 | } 88 | } 89 | 90 | func applySlot() { 91 | for i, s := range Slots { 92 | if s.slot.IsChecked() { 93 | hardwareSlot := i + Cfg.Device[SelectedDeviceId].Config.Slot.Offset 94 | sendSerialCmd(DeviceActions.SelectSlot + strconv.Itoa(hardwareSlot)) 95 | //select slot 96 | sendSerialCmd(Cfg.Device[SelectedDeviceId].CmdSet["config"] + "=" + s.mode.CurrentText()) 97 | //set mode 98 | sendSerialCmd(Cfg.Device[SelectedDeviceId].CmdSet["config"] + "=" + s.mode.CurrentText()) 99 | //set uid 100 | sendSerialCmd(Cfg.Device[SelectedDeviceId].CmdSet["uid"] + "=" + s.uid.Text()) 101 | //set button short 102 | sendSerialCmd(Cfg.Device[SelectedDeviceId].CmdSet["button"] + "=" + s.btns.CurrentText()) 103 | //set button long 104 | sendSerialCmd(Cfg.Device[SelectedDeviceId].CmdSet["buttonl"] + "=" + s.btnl.CurrentText()) 105 | } 106 | } 107 | populateSlots() 108 | } 109 | 110 | func countSelected() int { 111 | c := 0 112 | for _, s := range Slots { 113 | if s.slot.IsChecked() { 114 | c++ 115 | } 116 | } 117 | return c 118 | } 119 | 120 | //noinspection GoPrintFunctions 121 | func clearSlot() { 122 | c1 := 0 123 | for i, s := range Slots { 124 | sel := s.slot.IsChecked() 125 | if sel { 126 | c1++ 127 | log.Printf("clearing %s\n", s.slotl.Text()) 128 | hardwareSlot := i + Cfg.Device[SelectedDeviceId].Config.Slot.Offset 129 | sendSerialCmd(DeviceActions.SelectSlot + strconv.Itoa(hardwareSlot)) 130 | sendSerialCmd(DeviceActions.ClearSlot) 131 | } 132 | } 133 | time.Sleep(time.Millisecond * 50) 134 | populateSlots() 135 | } 136 | 137 | func refreshSlot() { 138 | populateSlots() 139 | } 140 | 141 | func activateSlots() { 142 | if countSelected() > 1 { 143 | widgets.QMessageBox_Information(nil, "OK", "please select only one Slot", 144 | widgets.QMessageBox__Ok, widgets.QMessageBox__Ok) 145 | return 146 | } 147 | 148 | for i, s := range Slots { 149 | sel := s.slot.IsChecked() 150 | if sel { 151 | hardwareSlot := i + Cfg.Device[SelectedDeviceId].Config.Slot.Offset 152 | sendSerialCmd(DeviceActions.SelectSlot + strconv.Itoa(hardwareSlot)) 153 | Cfg.Device[SelectedDeviceId].Config.Slot.Selected = hardwareSlot 154 | } 155 | } 156 | } 157 | 158 | //ToDO: implemetation 159 | //noinspection ALL 160 | func mfkey32Slots() { 161 | if !Connected || countSelected() < 1 { 162 | if !Connected { 163 | return 164 | } 165 | widgets.QMessageBox_Information(nil, "OK", "please select at least one Slot\nwhich was set to DETECTION", 166 | widgets.QMessageBox__Ok, widgets.QMessageBox__Ok) 167 | return 168 | } 169 | detectionCmd, ok := Cfg.Device[SelectedDeviceId].CmdSet["detection"] 170 | if !ok { 171 | widgets.QMessageBox_Information(nil, "OK", "Sorry, but this Device hs not set a 'detection' cmd!", 172 | widgets.QMessageBox__Ok, widgets.QMessageBox__Ok) 173 | return 174 | } 175 | for i, s := range Slots { 176 | sel := s.slot.IsChecked() 177 | if sel { 178 | hardwareSlot := i + Cfg.Device[SelectedDeviceId].Config.Slot.Offset 179 | sendSerialCmd(DeviceActions.SelectSlot + strconv.Itoa(hardwareSlot)) 180 | serialMonitor.AppendPlainText("-> " + strings.Replace(strings.Replace(detectionCmd, "\r", "", -1), "\n", "", -1)) 181 | 182 | // send cmd ang get the expected 218 bytes (208 nonce + 2 crc + 8 cmd-response (100:OK\n\r) 183 | SerialSendOnly(detectionCmd + "?") 184 | buff := GetSpecificBytes(218) 185 | //buffer should be empty - only to get sure 186 | SerialPort.ResetInputBuffer() 187 | 188 | //responsecode := strings.Replace(strings.Replace(string(buff[len(buff)-8:]), "\r", "", -1), "\n", "", -1) 189 | log.Println("len enc: ", len(buff)) 190 | buff = nonces.DecryptData(buff[0:len(buff)-10], 123321, 208) 191 | uid := buff[0:4] 192 | serialMonitor.AppendPlainText(fmt.Sprintf("uid: %x\n", uid)) 193 | 194 | noncemap := nonces.ExtractNonces(buff) 195 | var skey string 196 | if len(noncemap) > 0 { 197 | MyTabs.SetCurrentIndex(1) 198 | serialMonitor.AppendPlainText(fmt.Sprintf("Fond %d nonces for UID: %04X - test possible comboinations ...", len(noncemap), uid)) 199 | log.Println(" UID NT0 NR0 AR0 NT1 NR1 AR1") 200 | for i1 := 0; i1 < len(noncemap); i1++ { 201 | for i2 := 0; i2 < len(noncemap); i2++ { 202 | if i1 == i2 || i1 > i2 { 203 | continue 204 | } else { 205 | if noncemap[i1].Key == noncemap[i2].Key { 206 | if noncemap[i1].Key == 0x60 { 207 | skey = "A" 208 | } else { 209 | skey = "B" 210 | } 211 | args := []string{hex.EncodeToString(uid), hex.EncodeToString(noncemap[i1].Nt), hex.EncodeToString(noncemap[i1].Nr), hex.EncodeToString(noncemap[i1].Ar), hex.EncodeToString(noncemap[i2].Nt), hex.EncodeToString(noncemap[i2].Nr), hex.EncodeToString(noncemap[i2].Ar)} 212 | log.Printf("%04X %04X %04X %04X %04X %04X %04X\n", uid, noncemap[i1].Nt, noncemap[i1].Nr, noncemap[i1].Ar, noncemap[i2].Nt, noncemap[i2].Nr, noncemap[i2].Ar) 213 | res, err := execCmd("mfkey32v2", args) 214 | if err != nil { 215 | log.Println(err) 216 | } else { 217 | if strings.Contains(res, "Found Key") { 218 | key := strings.Split(res, "[")[1] 219 | key = key[:12] 220 | serialMonitor.AppendPlainText(fmt.Sprintf("Slot %d: Possible Key %s for Nonces on Blocks %d & %d = %s", i+1, skey, noncemap[i1].Block, noncemap[i2].Block, key)) 221 | 222 | } 223 | } 224 | } 225 | } 226 | } 227 | } 228 | } 229 | } 230 | } 231 | } 232 | 233 | //noinspection ALL 234 | func uploadSlots() bool { 235 | if countSelected() > 1 { 236 | widgets.QMessageBox_Information(nil, "OK", "please select only one Slot", 237 | widgets.QMessageBox__Ok, widgets.QMessageBox__Ok) 238 | return false 239 | } 240 | var filename string 241 | fileSelect := widgets.NewQFileDialog(nil, 0) 242 | filename = fileSelect.GetOpenFileName(nil, "open Dump", "", "", "", fileSelect.Options()) 243 | if filename == "" { 244 | log.Println("no file selected") 245 | return false 246 | } 247 | 248 | for i, s := range Slots { 249 | if s.slot.IsChecked() { 250 | hardwareSlot := i + Cfg.Device[SelectedDeviceId].Config.Slot.Offset 251 | sendSerialCmd(DeviceActions.SelectSlot + strconv.Itoa(hardwareSlot)) 252 | // Open file 253 | log.Printf("loading file %s\n", filename) 254 | fIn, err := os.Open(filename) 255 | if err != nil { 256 | log.Fatalln(err) 257 | } 258 | //readfile into buffer 259 | data, err := ioutil.ReadAll(fIn) 260 | if err != nil { 261 | log.Println(err) 262 | } 263 | fIn.Close() 264 | 265 | var p []xmodem.Xblock 266 | p = Bytes2Packets(data) 267 | 268 | //set chameleon into receiver-mode 269 | sendSerialCmd(DeviceActions.StartUpload) 270 | if SerialResponse.Code == 110 { 271 | //start uploading packets 272 | xmodem.Send(SerialPort, p) 273 | } 274 | } 275 | } 276 | refreshSlot() 277 | return true 278 | } 279 | 280 | func Bytes2Packets(data []byte) []xmodem.Xblock { 281 | var p []xmodem.Xblock 282 | var p1 xmodem.Xblock 283 | 284 | for _, d := range data { 285 | p1.Payload = append(p1.Payload, d) 286 | 287 | if len(p1.Payload) == 128 { 288 | p1.Proto = []byte{xmodem.SOH} 289 | p1.PacketNum = len(p) 290 | p1.PacketInv = 255 - p1.PacketNum 291 | p1.Checksum = int(xmodem.Checksum(p1.Payload, 0)) 292 | p = append(p, p1) 293 | p1.Payload = []byte("") 294 | } 295 | } 296 | return p 297 | } 298 | 299 | //noinspection ALL 300 | func downloadSlots() { 301 | if countSelected() > 1 { 302 | widgets.QMessageBox_Information(nil, "OK", "please select only one Slot", 303 | widgets.QMessageBox__Ok, widgets.QMessageBox__Ok) 304 | return 305 | } 306 | 307 | var ( 308 | success int 309 | failed int 310 | ) 311 | var filename string 312 | var data bytes.Buffer 313 | 314 | for i, s := range Slots { 315 | hardwareSlot := i + Cfg.Device[SelectedDeviceId].Config.Slot.Offset 316 | sel := s.slot.IsChecked() 317 | if sel { 318 | fileSelect := widgets.NewQFileDialog(nil, 0) 319 | filename = fileSelect.GetSaveFileName(nil, "save Data from "+s.slotl.Text()+" to File", "", "", "", fileSelect.Options()) 320 | if filename == "" { 321 | log.Println("no file seleted") 322 | return 323 | } 324 | log.Printf("download a dump from Slot %d into file %s\n", i, filename) 325 | 326 | //select slot 327 | sendSerialCmd(DeviceActions.SelectSlot + strconv.Itoa(hardwareSlot)) 328 | //set chameleon into receiver-mode 329 | sendSerialCmd(DeviceActions.StartDownload) 330 | if SerialResponse.Code == 110 { 331 | temp, _ := strconv.Atoi(s.size.Text()) 332 | success, failed, data = xmodem.Receive(SerialPort, temp) 333 | 334 | log.Printf("Success: %d - failed: %d\n", success, failed) 335 | } 336 | if _, err := SerialPort.Write([]byte{xmodem.CAN}); err != nil { 337 | log.Println(err) 338 | break 339 | } 340 | 341 | if data.Len() > 0 { 342 | log.Printf("got %d bytes to write to %s... ", data.Len(), filename) 343 | // Write file 344 | fOut, err := os.Create(filename) 345 | if err != nil { 346 | log.Println(filename, " - write failed") 347 | log.Fatalln(err) 348 | } 349 | fOut.Write(data.Bytes()) 350 | fOut.Close() 351 | 352 | log.Println(filename, " - write successful") 353 | } else { 354 | log.Println("bytes - file not written - bytes received: ", data.Len()) 355 | } 356 | } 357 | } 358 | } 359 | 360 | func getSlotBytes(sn int) (res []byte) { 361 | var data bytes.Buffer 362 | var failed = 0 363 | var success = 0 364 | hardwareSlot := sn + Cfg.Device[SelectedDeviceId].Config.Slot.Offset 365 | //select slot 366 | sendSerialCmd(DeviceActions.SelectSlot + strconv.Itoa(hardwareSlot)) 367 | //set chameleon into receiver-mode 368 | sendSerialCmd(DeviceActions.StartDownload) 369 | if SerialResponse.Code == 110 { 370 | temp, _ := strconv.Atoi(Slots[sn].size.Text()) 371 | log.Printf("expecting %d bytes", temp) 372 | success, failed, data = xmodem.Receive(SerialPort, temp) 373 | SerialPort.Write([]byte{xmodem.EOT}) 374 | log.Printf("HardwareSlot: %d - Success: %d - failed: %d - data-len: %d\n", hardwareSlot, success, failed, len(data.Bytes())) 375 | } 376 | if _, err := SerialPort.Write([]byte{xmodem.CAN}); err != nil { 377 | SerialPort.Write([]byte{xmodem.CAN}) 378 | log.Println(err) 379 | return nil 380 | } 381 | 382 | //ToDo: dirty woraround to avoid 'runs one time'-problem 383 | sendSerialCmd(DeviceActions.GetRssi) 384 | return data.Bytes() 385 | } 386 | 387 | func populateSlots() { 388 | if !Connected { 389 | return 390 | } 391 | 392 | if populated == false { 393 | //ToDo: error-handling 394 | sendSerialCmd(DeviceActions.GetModes) 395 | TagModes = strings.Split(SerialResponse.Payload, ",") 396 | //ToDo: error-handling 397 | sendSerialCmd(DeviceActions.GetButtons) 398 | TagButtons = strings.Split(SerialResponse.Payload, ",") 399 | populated = true 400 | } 401 | 402 | c := 0 403 | 404 | hardwareSlot := 0 405 | for sn, s := range Slots { 406 | //update single slot 407 | if s.slot.IsChecked() { 408 | c++ 409 | hardwareSlot = sn + Cfg.Device[SelectedDeviceId].Config.Slot.Offset 410 | 411 | log.Println("read data for Slot ", sn+1) 412 | sendSerialCmd(DeviceActions.SelectSlot + strconv.Itoa(hardwareSlot)) 413 | //get slot uid 414 | sendSerialCmd(DeviceActions.GetUid) 415 | uid := SerialResponse.Payload 416 | //set uid to lineedit 417 | s.uid.SetText(uid) 418 | 419 | sendSerialCmd(DeviceActions.GetSize) 420 | size := SerialResponse.Payload 421 | 422 | s.size.SetText(size) 423 | 424 | sendSerialCmd(DeviceActions.GetMode) 425 | mode := SerialResponse.Payload 426 | _, modeindex := getPosFromList(mode, TagModes) 427 | s.mode.Clear() 428 | s.mode.AddItems(TagModes) 429 | s.mode.SetCurrentIndex(modeindex) 430 | s.mode.Repaint() 431 | 432 | sendSerialCmd(DeviceActions.GetButtonl) 433 | buttonl := SerialResponse.Payload 434 | _, buttonlindex := getPosFromList(buttonl, TagButtons) 435 | s.btnl.Clear() 436 | s.btnl.AddItems(TagButtons) 437 | s.btnl.SetCurrentIndex(buttonlindex) 438 | s.btnl.Repaint() 439 | 440 | // ToDo: currently mostly faked - currently not implemented in my revG 441 | // unlear about RButton & LButton short and long -> 4 scenarios? 442 | // but works on RevG 443 | sendSerialCmd(DeviceActions.GetButton) 444 | buttons := SerialResponse.Payload 445 | _, buttonsindex := getPosFromList(buttons, TagButtons) 446 | s.btns.Clear() 447 | s.btns.AddItems(TagButtons) 448 | s.btns.SetCurrentIndex(buttonsindex) 449 | s.btns.Repaint() 450 | } 451 | } 452 | } 453 | 454 | func checkForDevices() { 455 | GetUsbListTicker = time.NewTicker(time.Millisecond * 5000) 456 | go func() { 457 | for myTime = range GetUsbListTicker.C { 458 | if !Connected { 459 | serialPorts, err := getSerialPorts() 460 | if err != nil { 461 | log.Println(err) 462 | } 463 | if len(serialPorts) > 0 && serialPortSelect.CurrentText() != serialPorts[SelectedPortId] { 464 | serialPortSelect.Clear() 465 | serialPortSelect.AddItems(serialPorts) 466 | serialPortSelect.SetCurrentIndex(SelectedPortId) 467 | serialPortSelect.Repaint() 468 | 469 | deviceSelect.SetCurrentIndex(SelectedDeviceId) 470 | deviceSelect.Repaint() 471 | } else { 472 | if len(serialPorts) == 0 { 473 | serialPortSelect.Clear() 474 | } 475 | } 476 | } else { 477 | GetUsbListTicker.Stop() 478 | } 479 | } 480 | }() 481 | } 482 | 483 | func getPosFromList(val string, array []string) (exists bool, index int) { 484 | exists = false 485 | index = -1 486 | 487 | for i, v := range array { 488 | if val == v { 489 | index = i 490 | exists = true 491 | return 492 | } 493 | } 494 | 495 | return 496 | } 497 | 498 | func execCmd(cmdstr string, args []string) (res string, err error) { 499 | res = "" 500 | err = nil 501 | 502 | //set local path 503 | os.Setenv("PATH", os.Getenv("PATH")+":"+os.Getenv("PWD")+"/bin/") 504 | 505 | // Create an *exec.Cmd 506 | cmd := exec.Command(cmdstr, args...) 507 | 508 | // Stdout buffer 509 | cmdOutput := &bytes.Buffer{} 510 | // Attach buffer to command 511 | cmd.Stdout = cmdOutput 512 | 513 | // Execute command 514 | //log.Printf("run Cmd: %s %s\n",cmd.Path,cmd.Args) 515 | err = cmd.Run() 516 | if err != nil { 517 | log.Println("Error: ", err) 518 | return res, err 519 | } 520 | 521 | // Only output the commands stdout 522 | res = string(cmdOutput.Bytes()) 523 | //log.Printf("RES: %s\n",res) 524 | return res, err 525 | } 526 | -------------------------------------------------------------------------------- /slotTab.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/WolfgangMau/chamgo-qt/config" 5 | "github.com/therecipe/qt/gui" 6 | "github.com/therecipe/qt/widgets" 7 | "log" 8 | "os" 9 | "runtime" 10 | "strconv" 11 | ) 12 | 13 | type Slot struct { 14 | widgets.QMainWindow 15 | slotl *widgets.QLabel 16 | slot *widgets.QCheckBox 17 | model *widgets.QLabel 18 | mode *widgets.QComboBox 19 | uidl *widgets.QLabel 20 | uid *widgets.QLineEdit 21 | btnsl *widgets.QLabel 22 | btns *widgets.QComboBox 23 | btnll *widgets.QLabel 24 | btnl *widgets.QComboBox 25 | sizel *widgets.QLabel 26 | size *widgets.QLineEdit 27 | } 28 | 29 | type SlotHLayout struct { 30 | l *widgets.QHBoxLayout 31 | } 32 | type SlotVLayout struct { 33 | l *widgets.QVBoxLayout 34 | } 35 | 36 | type SlotBox struct { 37 | b *widgets.QGroupBox 38 | } 39 | 40 | var ActionButtons = []string{"Select All", "Select None", "Apply", "Clear", "Refresh", "Set Active", "mfkey32", "Upload", "Download"} 41 | 42 | type ActionButton struct { 43 | b *widgets.QPushButton 44 | } 45 | 46 | var actionButtons [9]ActionButton 47 | 48 | var Slots [8]Slot 49 | var Slotlayouts [2]SlotHLayout 50 | var SlotGroupVlayouts [8]SlotVLayout 51 | var Slotboxes [8]SlotBox 52 | 53 | var populated = false 54 | var TagModes []string 55 | var TagButtons []string 56 | var RssiVal *widgets.QLineEdit 57 | 58 | func allSlots() *widgets.QWidget { 59 | bold := gui.NewQFont() 60 | bold.SetBold(true) 61 | 62 | slotsTabLayout := widgets.NewQGridLayout(nil) 63 | slotsTabPage := widgets.NewQWidget(nil, 0) 64 | var c = 0 65 | //two rows 66 | for i := 0; i <= 1; i++ { 67 | Slotlayouts[i].l = widgets.NewQHBoxLayout() 68 | Slotlayouts[i].l.SetAlign(33) 69 | 70 | SlotGroupVlayouts[i].l = widgets.NewQVBoxLayout() 71 | SlotGroupVlayouts[i].l.SetAlign(33) 72 | 73 | //4 columms 74 | for s := 0; s <= 3; s++ { 75 | boxlayout := widgets.NewQGridLayout(nil) 76 | boxlayout.SetAlign(33) 77 | 78 | /************* Slot checkbox ************/ 79 | Slots[c].slotl = widgets.NewQLabel(nil, 0) 80 | Slots[c].slotl.SetText("Slot " + strconv.Itoa(c+1)) 81 | Slots[c].slotl.SetContentsMargins(10, 0, 0, 0) 82 | Slots[c].slotl.SetFont(bold) 83 | Slots[c].slot = widgets.NewQCheckBox(nil) 84 | Slots[c].slot.SetChecked(false) 85 | 86 | boxlayout.AddWidget(Slots[c].slotl, 0, 0, 0x0001) 87 | boxlayout.AddWidget(Slots[c].slot, 0, 1, 0x0001) 88 | 89 | /************* Slot Group ************/ 90 | Slots[c].model = widgets.NewQLabel(nil, 0) 91 | Slots[c].model.SetText("Mode") 92 | Slots[c].mode = widgets.NewQComboBox(nil) 93 | Slots[c].mode.SetFixedWidth(147) 94 | boxlayout.AddWidget(Slots[c].model, 1, 0, 0x0001) 95 | boxlayout.AddWidget(Slots[c].mode, 1, 1, 0x0001) 96 | 97 | Slots[c].uidl = widgets.NewQLabel(nil, 0) 98 | Slots[c].uidl.SetText("UID") 99 | Slots[c].uid = widgets.NewQLineEdit(nil) 100 | Slots[c].uid.SetFixedWidth(141) 101 | boxlayout.AddWidget(Slots[c].uidl, 2, 0, 0x0001) 102 | boxlayout.AddWidget(Slots[c].uid, 2, 1, 0x0001) 103 | 104 | Slots[c].btnsl = widgets.NewQLabel(nil, 0) 105 | Slots[c].btnsl.SetText("Btn Short") 106 | Slots[c].btns = widgets.NewQComboBox(nil) 107 | Slots[c].btns.SetFixedWidth(147) 108 | boxlayout.AddWidget(Slots[c].btnsl, 3, 0, 0x0001) 109 | boxlayout.AddWidget(Slots[c].btns, 3, 1, 0x0001) 110 | 111 | Slots[c].btnll = widgets.NewQLabel(nil, 0) 112 | Slots[c].btnll.SetText("Btn Long") 113 | Slots[c].btnl = widgets.NewQComboBox(nil) 114 | Slots[c].btnl.SetFixedWidth(147) 115 | boxlayout.AddWidget(Slots[c].btnll, 4, 0, 0x0001) 116 | boxlayout.AddWidget(Slots[c].btnl, 4, 1, 0x0001) 117 | 118 | Slots[c].sizel = widgets.NewQLabel(nil, 0) 119 | Slots[c].sizel.SetText("Size") 120 | Slots[c].size = widgets.NewQLineEdit(nil) 121 | Slots[c].size.SetDisabled(true) 122 | Slots[c].size.SetFixedWidth(141) 123 | boxlayout.AddWidget(Slots[c].sizel, 5, 0, 0x0001) 124 | boxlayout.AddWidget(Slots[c].size, 5, 1, 0x0001) 125 | 126 | SlotGrouplayout := widgets.NewQVBoxLayout() 127 | SlotGrouplayout.AddLayout(boxlayout, 0) 128 | 129 | Slotboxes[i].b = widgets.NewQGroupBox(nil) 130 | Slotboxes[i].b.SetLayout(SlotGrouplayout) 131 | 132 | SlotGroupVlayouts[i].l = widgets.NewQVBoxLayout() 133 | SlotGroupVlayouts[i].l.SetSpacing(0) 134 | SlotGroupVlayouts[i].l.AddWidget(Slotboxes[i].b, 1, 0x0001) 135 | slotsTabLayout.AddLayout(SlotGroupVlayouts[i].l, i, s, 0x0020) 136 | 137 | c++ 138 | } 139 | } 140 | 141 | abtnLayout := widgets.NewQGridLayout(nil) 142 | 143 | var mfkeyBinPresent = true 144 | if len(Cfg.Device[SelectedDeviceId].Config.MfkeyBin) > 0 { 145 | if _, err := os.Stat(config.Apppath() + string(os.PathSeparator) + runtime.GOOS + string(os.PathSeparator) + "bin" + string(os.PathSeparator) + Cfg.Device[SelectedDeviceId].Config.MfkeyBin); os.IsNotExist(err) { 146 | mfkeyBinPresent = false 147 | log.Println("No mfkey32 binary configured or found in " + config.Apppath() + string(os.PathSeparator) + runtime.GOOS + string(os.PathSeparator) + "bin" + string(os.PathSeparator) + Cfg.Device[SelectedDeviceId].Config.MfkeyBin) 148 | } 149 | } 150 | 151 | var withdet = true 152 | _, ok := Cfg.Device[SelectedDeviceId].CmdSet["detection"] 153 | if !ok || !mfkeyBinPresent || Cfg.Device[SelectedDeviceId].Config.MfkeyBin == "" { 154 | withdet = false 155 | } 156 | log.Print("MfkeyBin: ", Cfg.Device[SelectedDeviceId].Config.MfkeyBin) 157 | 158 | for i, s := range ActionButtons { 159 | actionButtons[i].b = widgets.NewQPushButton2(s, nil) 160 | if s == "mfkey32" { 161 | actionButtons[i].b.SetEnabled(withdet) 162 | if !withdet { 163 | actionButtons[i].b.SetToolTip("No mfkey32 binary configured or found in\n" + config.Apppath() + string(os.PathSeparator) + runtime.GOOS + string(os.PathSeparator) + "bin" + string(os.PathSeparator) + Cfg.Device[SelectedDeviceId].Config.MfkeyBin) 164 | } 165 | } 166 | 167 | abtnLayout.AddWidget(actionButtons[i].b, 0, i, 0x0004) 168 | 169 | } 170 | AButtonGroup := widgets.NewQGroupBox2("Available Actions", nil) 171 | AButtonGroup.SetLayout(abtnLayout) 172 | 173 | A2ButtonLayout := widgets.NewQHBoxLayout() 174 | A2ButtonLayout.AddWidget(AButtonGroup, 0, 0x0004) 175 | slotsTabLayout.AddLayout2(A2ButtonLayout, 3, 0, 1, 4, 0x0004) 176 | 177 | Slots[0].slot.ConnectStateChanged(func(checked int) { 178 | slotChecked(0, checked) 179 | }) 180 | Slots[1].slot.ConnectStateChanged(func(checked int) { 181 | slotChecked(1, checked) 182 | }) 183 | Slots[2].slot.ConnectStateChanged(func(checked int) { 184 | slotChecked(2, checked) 185 | }) 186 | Slots[3].slot.ConnectStateChanged(func(checked int) { 187 | slotChecked(3, checked) 188 | }) 189 | Slots[4].slot.ConnectStateChanged(func(checked int) { 190 | slotChecked(4, checked) 191 | }) 192 | Slots[5].slot.ConnectStateChanged(func(checked int) { 193 | slotChecked(5, checked) 194 | }) 195 | Slots[6].slot.ConnectStateChanged(func(checked int) { 196 | slotChecked(6, checked) 197 | }) 198 | Slots[7].slot.ConnectStateChanged(func(checked int) { 199 | slotChecked(7, checked) 200 | }) 201 | 202 | actionButtons[0].b.ConnectClicked(func(checked bool) { 203 | buttonClicked(0) 204 | }) 205 | actionButtons[1].b.ConnectClicked(func(checked bool) { 206 | buttonClicked(1) 207 | }) 208 | actionButtons[2].b.ConnectClicked(func(checked bool) { 209 | buttonClicked(2) 210 | }) 211 | actionButtons[3].b.ConnectClicked(func(checked bool) { 212 | buttonClicked(3) 213 | }) 214 | actionButtons[4].b.ConnectClicked(func(checked bool) { 215 | buttonClicked(4) 216 | }) 217 | actionButtons[5].b.ConnectClicked(func(checked bool) { 218 | buttonClicked(5) 219 | }) 220 | actionButtons[6].b.ConnectClicked(func(checked bool) { 221 | buttonClicked(6) 222 | }) 223 | actionButtons[7].b.ConnectClicked(func(checked bool) { 224 | buttonClicked(7) 225 | }) 226 | actionButtons[8].b.ConnectClicked(func(checked bool) { 227 | buttonClicked(8) 228 | }) 229 | 230 | slotsTabPage.SetLayout(slotsTabLayout) 231 | 232 | return slotsTabPage 233 | } 234 | -------------------------------------------------------------------------------- /terminalTab.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "github.com/WolfgangMau/chamgo-qt/config" 7 | "github.com/WolfgangMau/chamgo-qt/crc16" 8 | "github.com/WolfgangMau/chamgo-qt/nonces" 9 | "github.com/WolfgangMau/chamgo-qt/xmodem" 10 | "github.com/therecipe/qt/widgets" 11 | "log" 12 | "os" 13 | "runtime" 14 | "strconv" 15 | "strings" 16 | "time" 17 | ) 18 | 19 | var ( 20 | serialSendButton *widgets.QPushButton 21 | serialMonitor *widgets.QPlainTextEdit 22 | serialPortSelect *widgets.QComboBox 23 | deviceSelect *widgets.QComboBox 24 | CrcVal *widgets.QLineEdit 25 | XorVal *widgets.QLineEdit 26 | ) 27 | 28 | //noinspection GoPrintFunctions 29 | func serialTab() *widgets.QWidget { 30 | serialTabLayout := widgets.NewQHBoxLayout() 31 | leftTabLayout := widgets.NewQVBoxLayout() 32 | serialTabPage := widgets.NewQWidget(nil, 0) 33 | 34 | /********************************************** Serial Connect *********************************************/ 35 | 36 | serialPorts, _ := getSerialPorts() 37 | 38 | serConLayout := widgets.NewQFormLayout(nil) 39 | 40 | deviceSelect = widgets.NewQComboBox(nil) 41 | deviceSelect.AddItems(getDeviceNames()) 42 | deviceSelect.SetCurrentIndex(SelectedDeviceId) 43 | deviceSelect.SetFixedWidth(160) 44 | 45 | serialPortSelect = widgets.NewQComboBox(nil) 46 | serialPortSelect.AddItems(serialPorts) 47 | serialPortSelect.SetFixedWidth(160) 48 | serialPortSelect.SetCurrentIndex(SelectedPortId) 49 | 50 | serialConnectButton := widgets.NewQPushButton2("Connect", nil) 51 | 52 | serialDeviceInfo := widgets.NewQLabel(nil, 0) 53 | serialDeviceInfo.SetText("not Connected") 54 | 55 | serConLayout.AddWidget(deviceSelect) 56 | serConLayout.AddWidget(serialPortSelect) 57 | serConLayout.AddWidget(serialConnectButton) 58 | serConLayout.AddWidget(serialDeviceInfo) 59 | 60 | serialConnectGroup := widgets.NewQGroupBox2("Serial Connection", nil) 61 | serialConnectGroup.SetLayout(serConLayout) 62 | serialConnectGroup.SetFixedSize2(220, 180) 63 | leftTabLayout.AddWidget(serialConnectGroup, 1, 0x0020) 64 | 65 | ToolsGroup := widgets.NewQGroupBox2("Fuctions", nil) 66 | ToolsLayout := widgets.NewQVBoxLayout() 67 | ToolsLayout.SetAlign(0x0020) 68 | ToolsLayout.SetSpacing(1) 69 | ToolsGroup.SetFixedWidth(220) 70 | 71 | //RSSI 72 | rssiLayout := widgets.NewQHBoxLayout() 73 | rssiBtn := widgets.NewQPushButton2("get RSSI", nil) 74 | rssiBtn.SetFixedWidth(100) 75 | rssiBtn.ConnectClicked(func(checked bool) { 76 | getRssi() 77 | }) 78 | rssiLayout.AddWidget(rssiBtn, 1, 0x0020) 79 | RssiVal = widgets.NewQLineEdit(nil) 80 | RssiVal.SetAlignment(0x0002) 81 | rssiLayout.AddWidget(RssiVal, 0, 0x0020) 82 | ToolsLayout.AddLayout(rssiLayout, 0) 83 | 84 | // CRC16 85 | crcLayout := widgets.NewQHBoxLayout() 86 | crcBtn := widgets.NewQPushButton2("calc CRC", nil) 87 | crcBtn.SetFixedWidth(100) 88 | crcBtn.ConnectClicked(func(checked bool) { 89 | CrcVal.SetText(crc16.GetCRCA(CrcVal.Text())) 90 | CrcVal.Repaint() 91 | }) 92 | crcLayout.AddWidget(crcBtn, 1, 0x0020) 93 | CrcVal = widgets.NewQLineEdit(nil) 94 | CrcVal.SetAlignment(0x0002) 95 | CrcVal.ConnectReturnPressed(crcBtn.Click) 96 | crcLayout.AddWidget(CrcVal, 0, 0x0020) 97 | ToolsLayout.AddLayout(crcLayout, 0) 98 | 99 | // XOR/BCC 100 | xorLayout := widgets.NewQHBoxLayout() 101 | xorBtn := widgets.NewQPushButton2("XOR/BCC", nil) 102 | xorBtn.SetFixedWidth(100) 103 | xorBtn.ConnectClicked(func(checked bool) { 104 | temp, _ := hex.DecodeString(XorVal.Text()) 105 | XorVal.SetText(strings.ToUpper(hex.EncodeToString([]byte{crc16.GetBCC(temp)}))) 106 | CrcVal.Repaint() 107 | }) 108 | xorLayout.AddWidget(xorBtn, 1, 0x0020) 109 | XorVal = widgets.NewQLineEdit(nil) 110 | XorVal.SetAlignment(0x0002) 111 | XorVal.ConnectReturnPressed(xorBtn.Click) 112 | xorLayout.AddWidget(XorVal, 0, 0x0020) 113 | ToolsLayout.AddLayout(xorLayout, 0) 114 | 115 | ToolsGroup.SetLayout(ToolsLayout) 116 | leftTabLayout.AddWidget(ToolsGroup, 1, 0x0020) 117 | 118 | macrodir := config.Apppath() + string(os.PathSeparator) + "macros" + string(os.PathSeparator) 119 | log.Println("checking for macrodir: ", macrodir) 120 | var macros []string 121 | if macrodir != "" { 122 | macros = config.GetFilesInFolder(macrodir, ".cmds") 123 | } 124 | if len(macros) > 0 { 125 | log.Println("Macro-Files found: ", len(macros)) 126 | 127 | macroGroupLayout := widgets.NewQHBoxLayout() 128 | macroGroup := widgets.NewQGroupBox2("Command Macros", nil) 129 | macroGroup.SetFixedWidth(220) 130 | macroSelect := widgets.NewQComboBox(macroGroup) 131 | macroSelect.AddItems(macros) 132 | macroGroupLayout.AddWidget(macroSelect, 1, 0x0020) 133 | macroSend := widgets.NewQPushButton2("execute", nil) 134 | macroGroupLayout.AddWidget(macroSend, 1, 0x0020) 135 | 136 | macroGroup.SetLayout(macroGroupLayout) 137 | leftTabLayout.AddWidget(macroGroup, 1, 0x0020) 138 | 139 | macroSend.ConnectClicked(func(checked bool) { 140 | 141 | if Connected { 142 | 143 | log.Println("execute macro ", macroSelect.CurrentText()) 144 | cmds := config.ReadFileLines(config.Apppath() + string(os.PathSeparator) + runtime.GOOS + string(os.PathSeparator) + "macros" + string(os.PathSeparator) + macroSelect.CurrentText()) 145 | if len(cmds) > 0 { 146 | for _, c := range cmds { 147 | if strings.Contains(strings.ToLower(c), "detectionmy?") { 148 | serialMonitor.AppendPlainText("-> " + strings.Replace(strings.Replace(c, "\r", "", -1), "\n", "", -1)) 149 | 150 | // send cmd ang get the expected 218 bytes (208 nonce + 2 crc + 8 cmd-response (100:OK\n\r) 151 | SerialSendOnly(c) 152 | buff := GetSpecificBytes(218) 153 | //buffer should be empty - only to get sure 154 | SerialPort.ResetInputBuffer() 155 | 156 | responsecode := strings.Replace(strings.Replace(string(buff[len(buff)-8:]), "\r", "", -1), "\n", "", -1) 157 | log.Println("len enc: ", len(buff)) 158 | buff = nonces.DecryptData(buff[0:len(buff)-10], 123321, 208) 159 | uid := buff[0:4] 160 | serialMonitor.AppendPlainText(fmt.Sprintf("uid: %04X\n", uid)) 161 | 162 | noncemap := nonces.ExtractNonces(buff) 163 | 164 | if len(noncemap) > 0 { 165 | serialMonitor.AppendPlainText(fmt.Sprintf("found %d nonces\n\t# NT NR AR", len(noncemap))) 166 | for i, n := range noncemap { 167 | serialMonitor.AppendPlainText(fmt.Sprintf("nonce #%d: %X %X %X", i+1, n.Nt, n.Nr, n.Ar)) 168 | } 169 | } 170 | serialMonitor.AppendPlainText(fmt.Sprintf("<- %s\nuid: %x\nbuff (%d): %X", responsecode, uid, len(buff), buff)) 171 | } else { 172 | if strings.Contains(strings.ToLower(c), "logdownload") { 173 | SerialSendOnly(c) 174 | time.Sleep(time.Millisecond * 500) 175 | success, failed, data := xmodem.Receive(SerialPort, 15) 176 | serialMonitor.AppendPlainText(fmt.Sprintf("\nLogReceive Blocks Success: %d Failed: %d\nData:\n%s\n", success, failed, string(hex.EncodeToString(data.Bytes())))) 177 | 178 | } else { 179 | sendSerialCmd(c) 180 | time.Sleep(time.Millisecond * time.Duration(Cfg.Device[SelectedDeviceId].Config.Serial.WaitForReceive)) 181 | serialMonitor.AppendPlainText(fmt.Sprintf("-> cmd: %s response: %s", c, SerialResponse.Payload)) 182 | } 183 | } 184 | serialMonitor.Repaint() 185 | } 186 | } 187 | } 188 | }) 189 | } 190 | 191 | serialTabLayout.AddLayout(leftTabLayout, 0) 192 | 193 | serialConnectButton.ConnectClicked(func(checked bool) { 194 | Commands := Cfg.Device[SelectedDeviceId].CmdSet 195 | //for n,c := range Commands { 196 | // log.Printf("Command Name: %s -> %s\n",n,c) 197 | //} 198 | //Commands.load(deviceSelect.CurrentText()) 199 | 200 | if serialConnectButton.Text() == "Connect" { 201 | 202 | err := connectSerial(SerialDevice) 203 | if err != nil { 204 | widgets.QMessageBox_Information(nil, "OK", "can't connect to Serial\n"+string(err.Error()), 205 | widgets.QMessageBox__Ok, widgets.QMessageBox__Ok) 206 | log.Println("error on connect: ", err) 207 | } else { 208 | initcfg() 209 | if len(DeviceActions.GetUid) <= 0 { 210 | log.Println("no action for 'getUid!?' ", DeviceActions.GetUid) 211 | } 212 | 213 | //ask for the device-version 214 | sendSerialCmd(Commands["version"] + "?") 215 | 216 | if SerialResponse.Code >= 100 { 217 | serialConnectButton.SetText("Disconnect") 218 | serialSendButton.SetDisabled(false) 219 | serialSendButton.Repaint() 220 | //checkForDevices() 221 | } 222 | //web got a expected answer from the VERSION(MY) cmd 223 | if SerialResponse.Code == 101 { 224 | serialDeviceInfo.SetText("Connected\n" + deviceInfo(SerialResponse.Payload)) 225 | Connected = true 226 | Statusbar.ShowMessage("Connected to Port: "+serialPortSelect.CurrentText()+" - Device: "+Cfg.Device[SelectedDeviceId].Cdc+" - Firmware: "+deviceInfo(SerialResponse.Payload), 0) 227 | if SerialResponse.Code == 101 { 228 | buttonClicked(0) 229 | buttonClicked(4) 230 | buttonClicked(1) 231 | MyTabs.SetCurrentIndex(0) 232 | } 233 | } else { 234 | widgets.QMessageBox_Information(nil, "OK", "no Version Response from Device!", 235 | widgets.QMessageBox__Ok, widgets.QMessageBox__Ok) 236 | } 237 | } 238 | } else { 239 | err := SerialPort.Close() 240 | if err == nil { 241 | Cfg.Save() 242 | serialConnectButton.SetText("Connect") 243 | serialSendButton.SetDisabled(true) 244 | serialSendButton.Repaint() 245 | serialDeviceInfo.SetText("not Connected") 246 | Statusbar.ShowMessage("not Connected", 0) 247 | Connected = false 248 | SerialPort.Close() 249 | 250 | } 251 | } 252 | 253 | }) 254 | 255 | /********************************************** Serial Monitor *********************************************/ 256 | 257 | serMonitorLayout := widgets.NewQVBoxLayout() 258 | serSendLayout := widgets.NewQHBoxLayout() 259 | 260 | serialMonitor = widgets.NewQPlainTextEdit(nil) 261 | serialMonitor.AppendPlainText("") 262 | serialMonitor.SetFixedHeight(380) 263 | serialMonitor.SetReadOnly(true) 264 | 265 | serialSendButton = widgets.NewQPushButton2("send", nil) 266 | serialSendButton.SetDisabled(true) 267 | serialSendButton.SetFixedWidth(80) 268 | 269 | serialSendTxt := widgets.NewQLineEdit(nil) 270 | serialSendTxt.ConnectReturnPressed(serialSendButton.Click) 271 | serialSendTxt.SetTabOrder(serialSendButton, nil) 272 | 273 | serSendLayout.AddWidget(serialSendTxt, 0, 0) 274 | serSendLayout.AddWidget(serialSendButton, 1, 0) 275 | 276 | serMonitorLayout.AddWidget(serialMonitor, 0, 0) 277 | serMonitorLayout.AddLayout(serSendLayout, 0) 278 | 279 | serialSendGroup := widgets.NewQGroupBox2("Serial Terminal", nil) 280 | serialSendGroup.SetLayout(serMonitorLayout) 281 | 282 | serialTabLayout.AddWidget(serialSendGroup, 0, 0x0020) 283 | serialTabLayout.SetAlign(33) 284 | 285 | serialSendButton.ConnectClicked(func(checked bool) { 286 | if serialSendTxt.Text() != "" { 287 | sendSerialCmd(serialSendTxt.Text()) 288 | if SerialResponse.Code >= 100 { 289 | serialMonitor.AppendPlainText("-> " + SerialResponse.Cmd) 290 | serialMonitor.AppendPlainText("<- " + strconv.Itoa(SerialResponse.Code) + " " + SerialResponse.String) 291 | if SerialResponse.Payload != "" { 292 | serialMonitor.AppendPlainText("<- " + SerialResponse.Payload) 293 | } 294 | serialMonitor.Repaint() 295 | } else { 296 | widgets.QMessageBox_Information(nil, "OK", "no Response for Cmd:\n"+serialSendTxt.Text(), 297 | widgets.QMessageBox__Ok, widgets.QMessageBox__Ok) 298 | } 299 | } 300 | }) 301 | 302 | serialTabPage.SetLayout(serialTabLayout) 303 | 304 | return serialTabPage 305 | } 306 | -------------------------------------------------------------------------------- /xmodem/xmodem.go: -------------------------------------------------------------------------------- 1 | package xmodem 2 | 3 | import ( 4 | "bytes" 5 | "go.bug.st/serial.v1" 6 | "log" 7 | "time" 8 | ) 9 | 10 | //xmodem 11 | const SOH byte = 0x01 12 | const STX byte = 0x02 13 | const EOT byte = 0x04 14 | const EOF byte = 0x1a 15 | const ACK byte = 0x06 16 | const NAK byte = 0x15 17 | const CAN byte = 0x18 18 | 19 | type Xblock struct { 20 | Proto []byte // 1 byte protocol (SOH / STX) 21 | PacketNum int // 1 byte current Packet number 22 | PacketInv int // 1 byte (0xff-packetNum) 23 | Payload []byte // 128 byte payload 24 | Checksum int // 1 byte complement checksum of the payload 25 | } 26 | 27 | func Receive(serialPort serial.Port, size int) (success int, failed int, data bytes.Buffer) { 28 | 29 | oBuffer := make([]byte, 1) 30 | dBuffer := make([]byte, size) 31 | 32 | //log.Println("prepare") 33 | var protocmd []byte 34 | protocmd = append(protocmd, NAK) 35 | 36 | success = 0 37 | failed = 0 38 | 39 | var getBytes = true 40 | for data.Len() < size { 41 | myPacket := Xblock{} 42 | 43 | // init tranafer 44 | if _, err := serialPort.Write(protocmd); err != nil { 45 | log.Println(err) 46 | break 47 | } 48 | 49 | if protocmd[0] == EOT || protocmd[0] == EOF || protocmd[0] == CAN { 50 | log.Printf("tranfer end.") 51 | getBytes=false 52 | if success == 0 { 53 | success++ 54 | data.Write(myPacket.Payload) 55 | } 56 | break 57 | } 58 | 59 | if _, err := serialPort.Read(oBuffer); err != nil { 60 | log.Println(err) 61 | break 62 | } 63 | 64 | //start receiving blocks 65 | if getBytes { 66 | bytesReceived := 0 67 | blockReceived := false 68 | for !blockReceived { 69 | time.Sleep(time.Millisecond * 25) 70 | n, err := serialPort.Read(dBuffer) 71 | bytesReceived = bytesReceived + n 72 | if err != nil { 73 | log.Println("Read failed:", err) 74 | } 75 | 76 | if bytesReceived >= 131 { 77 | myPacket.Proto = oBuffer 78 | myPacket.PacketNum = int(dBuffer[0]) 79 | myPacket.PacketInv = int(dBuffer[1]) 80 | myPacket.Payload = dBuffer[2:130] 81 | myPacket.Checksum = int(dBuffer[130]) 82 | 83 | CHK := int(Checksum(myPacket.Payload, 0)) 84 | if CHK == myPacket.Checksum && myPacket.checkPaylod() { 85 | //packet OK 86 | log.Println("Checksum OK for Packet: ", myPacket.PacketNum) 87 | protocmd[0] = ACK 88 | success++ 89 | data.Write(myPacket.Payload) 90 | } else { 91 | //something went wrong 92 | log.Println("something went wront with Packet: ", myPacket.PacketNum) 93 | if !myPacket.checkPaylod() && failed < 10 { 94 | 95 | if byte(myPacket.PacketNum) == EOF || byte(myPacket.PacketNum) == EOT { 96 | log.Println("transmission end at Block : ", myPacket.PacketNum) 97 | //EOT & EOF are no failures 98 | failed-- 99 | } else { 100 | //message for sender 101 | log.Println("resend ... Block ", myPacket.PacketNum) 102 | failed++ 103 | blockReceived = true 104 | protocmd[0] = NAK 105 | 106 | } 107 | } 108 | //stop transfer 109 | log.Printf("Failed Packet (%d)\n len: %d\nData: %X\n", myPacket.PacketNum, bytesReceived, dBuffer[:bytesReceived]) 110 | failed-- //the last packet checksum must have missmatched - no error! 111 | protocmd[0] = CAN 112 | getBytes = false 113 | } 114 | blockReceived = true 115 | } 116 | } 117 | } 118 | } 119 | return success, failed, data 120 | } 121 | 122 | func Send(serialPort serial.Port, p []Xblock) { 123 | oBuffer := make([]byte, 1) 124 | failure := 0 125 | success := 0 126 | //log.Printf("start sending %d Packets of %d bytes payload\n", len(p), len(p[0].payload)) 127 | for _, sp := range p { 128 | var resend = true //init - we need to read at least once 129 | for resend { 130 | //log.Printf("send Packet: %d\n", sp.packetNum) 131 | sendPacket(serialPort, sp) 132 | if _, err := serialPort.Read(oBuffer); err != nil { 133 | log.Println(err) 134 | } else { 135 | switch oBuffer[0] { 136 | case NAK: // NAK 137 | //receiver ask for retransmission of this block 138 | log.Printf("resend Packet %d\n", sp.PacketNum) 139 | resend = true 140 | failure++ 141 | case ACK: // ACK 142 | //receiver accepted this block 143 | resend = false // packet was accepted, no need to resend the packet 144 | success++ 145 | case CAN: // CAN 146 | //receiver wants to quit session 147 | log.Printf("receiver aborted transmission at Packet %d\n", sp.PacketNum) 148 | resend = false //quit session, no need to resend the packet 149 | failure++ 150 | default: 151 | //should not happen 152 | log.Printf("unexspected answer(0x%X) for packet %d\n", oBuffer[0], sp.PacketNum) 153 | resend = true // better to read the packet again, hopefully no endless loop ;-) 154 | failure++ 155 | } 156 | } 157 | } 158 | //when receiver sends CAN - stop transmitting 159 | if oBuffer[0] == CAN { 160 | break 161 | } 162 | } 163 | log.Printf("upload done - Success: %d - Failures: %d\n", success, failure) 164 | 165 | //send EOT byte 166 | var eot []byte 167 | var err error 168 | eot = append(eot, CAN) 169 | serialPort.Write(eot) 170 | n := 0 171 | for n == 1 { 172 | if n, err = serialPort.Read(oBuffer); err != nil { 173 | log.Println(err) 174 | } 175 | if oBuffer[0] != ACK { 176 | log.Printf("unexpected answer to EOT: 0x%X\n", oBuffer[0]) 177 | } else { 178 | log.Println("end of transfer") 179 | 180 | } 181 | } 182 | } 183 | 184 | func Checksum(b []byte, cs byte) byte { 185 | for _, d := range b { 186 | cs = cs + d 187 | } 188 | return cs 189 | } 190 | 191 | //returns false if all payload-bytes are set to 0xff 192 | func (p Xblock) checkPaylod() bool { 193 | var counter = 0 194 | for _, b := range p.Payload { 195 | if b == 0xff { 196 | counter++ 197 | } 198 | } 199 | if counter == len(p.Payload) { 200 | return false 201 | } 202 | return true 203 | } 204 | 205 | func sendPacket(serialPort serial.Port, p Xblock) { 206 | 207 | var sp []byte 208 | sp = append(sp, p.Proto[0]) 209 | sp = append(sp, byte(p.PacketNum)+1) 210 | sp = append(sp, byte(byte(255)-byte(p.PacketNum)-1)) 211 | for _, b := range p.Payload { 212 | sp = append(sp, b) 213 | } 214 | sp = append(sp, byte(p.Checksum)) 215 | serialPort.Write(sp) 216 | } 217 | --------------------------------------------------------------------------------