aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgutmet <me.git@gutmet.org>2020-04-27 19:33:47 +0200
committergutmet <me.git@gutmet.org>2020-04-27 19:33:47 +0200
commit279429d0bff0681d533b23c260249361078d0a78 (patch)
tree6693fca4fb0795992534e7541d9042731fd4d7c3
parent88c7d63adcb7b57610c6ecb1d57583b62e1ff070 (diff)
downloadlaymanshex-279429d0bff0681d533b23c260249361078d0a78.tar.gz
allow variable byte field size
* sequences of certain sizes or delimited with a marker can now be obtained through multiple invocations
-rw-r--r--README52
-rw-r--r--laymanshex.go126
2 files changed, 127 insertions, 51 deletions
diff --git a/README b/README
index 1e2b993..7540ed8 100644
--- a/README
+++ b/README
@@ -22,32 +22,56 @@ comment = '#' { LETTER | DIGIT } '\n'.
endianness = ("little endian" | "big endian") '\n'.
-definition = name ':' type '\n'.
+definition = [ IDENT ] ':' type '\n'.
-name = { LETTER | DIGIT }.
-
-type = ( "byte[" INTEGER "]" )
+type = bytefield
| int8 | int16 | int32 | int64
| uint8 | uint16 | uint32 | uint64
- | float32 | float64
+ | float32 | float64
+.
+
+bytefield = "byte[" ( INTEGER | IDENT ) "]".
```
-The name of a definition line can be left empty to ignore that particular value.
-Portions of the binary file not covered be the file description are ignored.
+The identifier of a definition line can be left empty to ignore that particular value.
+Portions of the binary file not covered by the file description are ignored. Byte field
+size can be variable - the specific value then needs to be set at program invocation (via -fvar).
Usage
-----
-To get values: laymanshex FORMATFILE BINARY
+To get values: `laymanshex FORMATFILE BINARY`
+
+To set values: `laymanshex -set "key1=value1,key2=value2" FORMATFILE BINARY`
-To set values: laymanshex -set "key1=value1,key2=value2" FORMATFILE BINARY
+To set a variable 'offset' to 113 in the file description and get values: `laymanshex -fvar="offset=113" FORMATFILE BINARY`
Examples of format descriptions can be found in the [laymanshex-files repo](/laymanshex-files/).
-TODO
-----
-There is no way to express offset values inside a file yet. Since this is
-relatively common, I will add it at some point. Also: Repeated data limited by
-marker or size field.
+Advanced
+--------
+
+The variable byte field size allows to script around laymanshex and use a sliding frame
+to obtain sequences. E.g. you can define a partial file format that only covers one
+element of the sequence at a certain offset:
+
+```
+little endian
+ : byte[offset]
+value : int32
+```
+
+Script:
+
+```
+#/bin/bash
+
+for i in `seq 99`; do
+ laymanshex -fvar="offset=$((i*4))" FORMATFILE BINARY
+done
+```
+
+If your sequence ends with a marker, add that marker to the file description and check
+its value after each invocation of laymanshex.
diff --git a/laymanshex.go b/laymanshex.go
index ef4e284..84d71c6 100644
--- a/laymanshex.go
+++ b/laymanshex.go
@@ -159,29 +159,48 @@ func (part *filePart) Line() string {
return part.name + " : " + part.parttype
}
-func (part *filePart) setHandling() {
- if strings.HasPrefix(part.parttype, "byte") {
- tmp := strings.TrimRight(strings.TrimLeft(part.parttype, "byte["), "]")
- bytes, err := strconv.ParseInt(tmp, 10, 64)
- optPanic(part.name, err)
- readFrom := func(r io.Reader, bo binary.ByteOrder) string {
- buf := make([]byte, bytes)
- var i int64
- for i = 0; i < bytes; i++ {
- binaryRead(r, bo, &buf[i])
- }
- return hex.EncodeToString(buf)
+type formatAssignment struct {
+ val int64
+ used bool
+}
+
+func (part *filePart) setByteFieldHandling(assignments map[string]*formatAssignment) {
+ tmp := trim(strings.TrimSuffix(strings.TrimPrefix(part.parttype, "byte["), "]"))
+ var bytes int64
+ var err error
+ if bytes, err = strconv.ParseInt(tmp, 10, 64); err != nil {
+ if ass, ok := assignments[tmp]; ok {
+ bytes = ass.val
+ ass.used = true
+ err = nil
+ } else {
+ err = errors.New("Could neither parse size to int64 nor obtain value from -fvar")
}
- writeTo := func(w io.Writer, bo binary.ByteOrder, s string) {
- s = strings.TrimPrefix(s, "0x")
- buf, err := hex.DecodeString(s)
- optPanic("invalid string of hex values: "+s, err)
- if int64(len(buf)) > bytes {
- panic(fmt.Sprintf("string %s is too long for byte array %s : want %d bytes", s, part.name, bytes))
- }
- binaryWrite(w, bo, buf)
+ }
+ optPanic(part.name, err)
+ readFrom := func(r io.Reader, bo binary.ByteOrder) string {
+ buf := make([]byte, bytes)
+ var i int64
+ for i = 0; i < bytes; i++ {
+ binaryRead(r, bo, &buf[i])
}
- part.handling = handling{bytes, readFrom, writeTo}
+ return hex.EncodeToString(buf)
+ }
+ writeTo := func(w io.Writer, bo binary.ByteOrder, s string) {
+ s = strings.TrimPrefix(s, "0x")
+ buf, err := hex.DecodeString(s)
+ optPanic("invalid string of hex values: "+s, err)
+ if int64(len(buf)) > bytes {
+ panic(fmt.Sprintf("string %s is too long for byte array %s : want %d bytes", s, part.name, bytes))
+ }
+ binaryWrite(w, bo, buf)
+ }
+ part.handling = handling{bytes, readFrom, writeTo}
+}
+
+func (part *filePart) setHandling(assignments map[string]*formatAssignment) {
+ if strings.HasPrefix(part.parttype, "byte") {
+ part.setByteFieldHandling(assignments)
} else {
if handling, ok := handlings[part.parttype]; !ok {
@@ -209,7 +228,7 @@ func info(i string, s string) {
}
}
-func readPart(line string) *filePart {
+func readPart(line string, assignments map[string]*formatAssignment) *filePart {
part := &filePart{}
splitline := strings.Split(line, ":")
if len(splitline) != 2 {
@@ -218,11 +237,19 @@ func readPart(line string) *filePart {
part.name = trim(splitline[0])
part.parttype = trim(splitline[1])
info("definition", part.Line())
- part.setHandling()
+ part.setHandling(assignments)
return part
}
-func readFileDescription(path string) fileDescription {
+func checkFormatAssignments(assignments map[string]*formatAssignment) {
+ for name, ass := range assignments {
+ if !ass.used {
+ panic("assignment to unknown variable: " + name)
+ }
+ }
+}
+
+func readFileDescription(path string, assignments map[string]*formatAssignment) fileDescription {
errmsg := "read format file"
descr := fileDescription{}
f, err := os.Open(path)
@@ -254,39 +281,62 @@ func readFileDescription(path string) fileDescription {
optPanic(errmsg, err)
line = trim(line)
for err != io.EOF && line != "" {
- part := readPart(line)
+ part := readPart(line, assignments)
descr.parts = append(descr.parts, *part)
line, err = buf.ReadString('\n')
line = trim(line)
}
line = trim(line)
if err == io.EOF && line != "" {
- descr.parts = append(descr.parts, *readPart(line))
+ descr.parts = append(descr.parts, *readPart(line, assignments))
} else if err != io.EOF {
optPanic(errmsg, err)
}
+ /*--------------------------------*/
+ checkFormatAssignments(assignments)
return descr
}
-func readAssignments(descr fileDescription, s string) map[string]string {
- assignments := strings.Split(s, ",")
+func readAssignments(name string, s string) map[string]string {
m := make(map[string]string)
+ s = trim(s)
+ if s == "" {
+ return m
+ }
+ assignments := strings.Split(s, ",")
for _, assignment := range assignments {
kv := strings.Split(assignment, "=")
if len(kv) != 2 {
- panic("weird parameters in set: " + assignment + "\nWant list of comma-separated key=value assignments")
+ panic("weird parameters in " + name + ": " + assignment + "\nWant list of comma-separated key=value assignments")
}
key := trim(kv[0])
value := trim(kv[1])
+ m[key] = value
+ }
+ return m
+}
+
+func readFormatAssignments(s string) map[string]*formatAssignment {
+ tmp := readAssignments("fvar", s)
+ m := make(map[string]*formatAssignment)
+ for key, val := range tmp {
+ i, err := strconv.ParseInt(val, 0, 64)
+ optPanic("Could not parse format variable assignment "+val, err)
+ m[key] = &formatAssignment{i, false}
+ }
+ return m
+}
+
+func readSetAssignments(descr fileDescription, s string) map[string]string {
+ m := readAssignments("set", s)
+ for key, _ := range m {
found := false
for _, part := range descr.parts {
if key == part.name {
found = true
}
}
- if found {
- m[key] = value
- } else {
+ if !found {
panic("unknown key: " + key)
}
}
@@ -381,8 +431,10 @@ func printUsage() {
}
func main() {
- var assignments string
- flag.StringVar(&assignments, "set", "", "key=value pairs (e.g. \"key1=value1,key2=value2\")")
+ var setAssignments string
+ var formatAssignments string
+ flag.StringVar(&setAssignments, "set", "", "key=value pairs (e.g. \"key1=value1,key2=value2\")")
+ flag.StringVar(&formatAssignments, "fvar", "", "key=value pairs (e.g. \"key1=value1,key2=value2\")")
flag.BoolVar(&debug, "debug", false, "print recognized parts of the format file")
flag.Parse()
args := flag.Args()
@@ -390,9 +442,9 @@ func main() {
printUsage()
os.Exit(-1)
}
- descr := readFileDescription(args[0])
- if assignments != "" {
- setValues(descr, args[1], readAssignments(descr, assignments))
+ descr := readFileDescription(args[0], readFormatAssignments(formatAssignments))
+ if setAssignments != "" {
+ setValues(descr, args[1], readSetAssignments(descr, setAssignments))
}
printValues(descr, getValues(descr, args[1]))
}