mirror of https://github.com/dsoprea/go-exif.git
type.go: Bugfix for math with embedded data. Add Format().
- ifd_builder.go - `BuilderTag` now embeds `ByteOrder`. - `BuilderTag` `String()` now prints value. - type.go - `TagType` `ValueIsEmbedded()` renamed to `valueIsEmbedded()`. - Add `readRawEncoded()` method to `TagType`. - Add `Format()` method to `TagType` to isolate code for processing output strings for all types. - This will also print a suffixing ellipsis when `justFirst` is `true`. - Important bugfix for embedded values always being presented as four-bytes rather than being truncated to their reported unit-count bytes first. We tripped over this when some of our corresponding math was found to be wrong elsewhere. - Minor reformatting.pull/28/head
parent
636417e4ae
commit
483dbacf14
|
@ -593,8 +593,8 @@
|
|||
"tag_type_id": 5,
|
||||
"tag_type_name": "RATIONAL",
|
||||
"unit_count": 4,
|
||||
"value": "16/1",
|
||||
"value_string": "16/1"
|
||||
"value": "16/1...",
|
||||
"value_string": "16/1..."
|
||||
},
|
||||
{
|
||||
"ifd_path": "IFD/Exif",
|
||||
|
@ -641,8 +641,8 @@
|
|||
"tag_type_id": 1,
|
||||
"tag_type_name": "BYTE",
|
||||
"unit_count": 4,
|
||||
"value": "0x02",
|
||||
"value_string": "0x02"
|
||||
"value": "02 03 00 00",
|
||||
"value_string": "02 03 00 00"
|
||||
},
|
||||
{
|
||||
"ifd_path": "IFD",
|
||||
|
|
|
@ -86,11 +86,11 @@ IFD-PATH=[IFD/Exif] ID=(0xa403) NAME=[WhiteBalance] COUNT=(1) TYPE=[SHORT] VALUE
|
|||
IFD-PATH=[IFD/Exif] ID=(0xa406) NAME=[SceneCaptureType] COUNT=(1) TYPE=[SHORT] VALUE=[0]
|
||||
IFD-PATH=[IFD/Exif] ID=(0xa430) NAME=[CameraOwnerName] COUNT=(1) TYPE=[ASCII] VALUE=[]
|
||||
IFD-PATH=[IFD/Exif] ID=(0xa431) NAME=[BodySerialNumber] COUNT=(13) TYPE=[ASCII] VALUE=[063024020097]
|
||||
IFD-PATH=[IFD/Exif] ID=(0xa432) NAME=[LensSpecification] COUNT=(4) TYPE=[RATIONAL] VALUE=[16/1]
|
||||
IFD-PATH=[IFD/Exif] ID=(0xa432) NAME=[LensSpecification] COUNT=(4) TYPE=[RATIONAL] VALUE=[16/1...]
|
||||
IFD-PATH=[IFD/Exif] ID=(0xa434) NAME=[LensModel] COUNT=(22) TYPE=[ASCII] VALUE=[EF16-35mm f/4L IS USM]
|
||||
IFD-PATH=[IFD/Exif] ID=(0xa435) NAME=[LensSerialNumber] COUNT=(11) TYPE=[ASCII] VALUE=[2400001068]
|
||||
IFD-PATH=[IFD] ID=(0x8825) NAME=[GPSTag] COUNT=(1) TYPE=[LONG] VALUE=[9554]
|
||||
IFD-PATH=[IFD/GPSInfo] ID=(0x0000) NAME=[GPSVersionID] COUNT=(4) TYPE=[BYTE] VALUE=[0x02]
|
||||
IFD-PATH=[IFD/GPSInfo] ID=(0x0000) NAME=[GPSVersionID] COUNT=(4) TYPE=[BYTE] VALUE=[02 03 00 00]
|
||||
IFD-PATH=[IFD] ID=(0x0103) NAME=[Compression] COUNT=(1) TYPE=[SHORT] VALUE=[6]
|
||||
IFD-PATH=[IFD] ID=(0x011a) NAME=[XResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]
|
||||
IFD-PATH=[IFD] ID=(0x011b) NAME=[YResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]
|
||||
|
|
|
@ -152,11 +152,11 @@ func TestVisit(t *testing.T) {
|
|||
"IFD-PATH=[IFD/Exif] ID=(0xa406) NAME=[SceneCaptureType] COUNT=(1) TYPE=[SHORT] VALUE=[0]",
|
||||
"IFD-PATH=[IFD/Exif] ID=(0xa430) NAME=[CameraOwnerName] COUNT=(1) TYPE=[ASCII] VALUE=[]",
|
||||
"IFD-PATH=[IFD/Exif] ID=(0xa431) NAME=[BodySerialNumber] COUNT=(13) TYPE=[ASCII] VALUE=[063024020097]",
|
||||
"IFD-PATH=[IFD/Exif] ID=(0xa432) NAME=[LensSpecification] COUNT=(4) TYPE=[RATIONAL] VALUE=[16/1]",
|
||||
"IFD-PATH=[IFD/Exif] ID=(0xa432) NAME=[LensSpecification] COUNT=(4) TYPE=[RATIONAL] VALUE=[16/1...]",
|
||||
"IFD-PATH=[IFD/Exif] ID=(0xa434) NAME=[LensModel] COUNT=(22) TYPE=[ASCII] VALUE=[EF16-35mm f/4L IS USM]",
|
||||
"IFD-PATH=[IFD/Exif] ID=(0xa435) NAME=[LensSerialNumber] COUNT=(11) TYPE=[ASCII] VALUE=[2400001068]",
|
||||
"IFD-PATH=[IFD] ID=(0x8825) NAME=[GPSTag] COUNT=(1) TYPE=[LONG] VALUE=[9554]",
|
||||
"IFD-PATH=[IFD/GPSInfo] ID=(0x0000) NAME=[GPSVersionID] COUNT=(4) TYPE=[BYTE] VALUE=[0x02]",
|
||||
"IFD-PATH=[IFD/GPSInfo] ID=(0x0000) NAME=[GPSVersionID] COUNT=(4) TYPE=[BYTE] VALUE=[02 03 00 00]",
|
||||
"IFD-PATH=[IFD] ID=(0x0103) NAME=[Compression] COUNT=(1) TYPE=[SHORT] VALUE=[6]",
|
||||
"IFD-PATH=[IFD] ID=(0x011a) NAME=[XResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]",
|
||||
"IFD-PATH=[IFD] ID=(0x011b) NAME=[YResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]",
|
||||
|
|
|
@ -59,6 +59,8 @@ func NewIfdBuilderTagValueFromIfdBuilder(ib *IfdBuilder) *IfdBuilderTagValue {
|
|||
}
|
||||
}
|
||||
|
||||
// IsBytes returns true if the bytes are populated. This is always the case
|
||||
// when we're loaded from a tag in an existing IFD.
|
||||
func (ibtv IfdBuilderTagValue) IsBytes() bool {
|
||||
return ibtv.valueBytes != nil
|
||||
}
|
||||
|
@ -94,14 +96,19 @@ type BuilderTag struct {
|
|||
// child IFDs), or an IfdTagEntry instance representing an existing,
|
||||
// previously-stored tag.
|
||||
value *IfdBuilderTagValue
|
||||
|
||||
// byteOrder is the byte order. It's chiefly/originally here to support
|
||||
// printing the value.
|
||||
byteOrder binary.ByteOrder
|
||||
}
|
||||
|
||||
func NewBuilderTag(ifdPath string, tagId uint16, typeId uint16, value *IfdBuilderTagValue) *BuilderTag {
|
||||
func NewBuilderTag(ifdPath string, tagId uint16, typeId uint16, value *IfdBuilderTagValue, byteOrder binary.ByteOrder) *BuilderTag {
|
||||
return &BuilderTag{
|
||||
ifdPath: ifdPath,
|
||||
tagId: tagId,
|
||||
typeId: typeId,
|
||||
value: value,
|
||||
ifdPath: ifdPath,
|
||||
tagId: tagId,
|
||||
typeId: typeId,
|
||||
value: value,
|
||||
byteOrder: byteOrder,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,7 +126,20 @@ func (bt *BuilderTag) Value() (value *IfdBuilderTagValue) {
|
|||
}
|
||||
|
||||
func (bt *BuilderTag) String() string {
|
||||
return fmt.Sprintf("BuilderTag<IFD-PATH=[%s] TAG-ID=(0x%04x) TAG-TYPE=[%s] VALUE=[%s]>", bt.ifdPath, bt.tagId, TypeNames[bt.typeId], bt.value)
|
||||
var valueString string
|
||||
|
||||
if bt.value.IsBytes() == true {
|
||||
tt := NewTagType(bt.typeId, bt.byteOrder)
|
||||
|
||||
var err error
|
||||
|
||||
valueString, err = tt.Format(bt.value.Bytes(), false)
|
||||
log.PanicIf(err)
|
||||
} else {
|
||||
valueString = fmt.Sprintf("%v", bt.value)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("BuilderTag<IFD-PATH=[%s] TAG-ID=(0x%04x) TAG-TYPE=[%s] VALUE=[%s]>", bt.ifdPath, bt.tagId, TypeNames[bt.typeId], valueString)
|
||||
}
|
||||
|
||||
func (bt *BuilderTag) SetValue(byteOrder binary.ByteOrder, value interface{}) (err error) {
|
||||
|
@ -179,7 +199,8 @@ func NewStandardBuilderTag(ifdPath string, it *IndexedTag, byteOrder binary.Byte
|
|||
ifdPath,
|
||||
it.Id,
|
||||
typeId,
|
||||
tagValue)
|
||||
tagValue,
|
||||
byteOrder)
|
||||
}
|
||||
|
||||
type IfdBuilder struct {
|
||||
|
@ -513,7 +534,13 @@ func (ib *IfdBuilder) SetThumbnail(data []byte) (err error) {
|
|||
ib.thumbnailData = data
|
||||
|
||||
ibtvfb := NewIfdBuilderTagValueFromBytes(ib.thumbnailData)
|
||||
offsetBt := NewBuilderTag(ib.ifdPath, ThumbnailOffsetTagId, TypeLong, ibtvfb)
|
||||
offsetBt :=
|
||||
NewBuilderTag(
|
||||
ib.ifdPath,
|
||||
ThumbnailOffsetTagId,
|
||||
TypeLong,
|
||||
ibtvfb,
|
||||
ib.byteOrder)
|
||||
|
||||
err = ib.Set(offsetBt)
|
||||
log.PanicIf(err)
|
||||
|
@ -1051,7 +1078,12 @@ func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, itevr *IfdTagEntryValueResol
|
|||
// (nor does it matter since this is just a temporary
|
||||
// placeholder, in this situation).
|
||||
value := NewIfdBuilderTagValueFromBytes([]byte{0, 0, 0, 0})
|
||||
bt = NewBuilderTag(ite.IfdPath, ite.TagId, ite.TagType, value)
|
||||
bt = NewBuilderTag(
|
||||
ite.IfdPath,
|
||||
ite.TagId,
|
||||
ite.TagType,
|
||||
value,
|
||||
ib.byteOrder)
|
||||
} else {
|
||||
// Figure out which of the child-IFDs that are associated with
|
||||
// this IFD represents this specific child IFD.
|
||||
|
@ -1107,7 +1139,12 @@ func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, itevr *IfdTagEntryValueResol
|
|||
value = NewIfdBuilderTagValueFromBytes(valueBytes)
|
||||
}
|
||||
|
||||
bt = NewBuilderTag(ifd.IfdPath, ite.TagId, ite.TagType, value)
|
||||
bt = NewBuilderTag(
|
||||
ifd.IfdPath,
|
||||
ite.TagId,
|
||||
ite.TagType,
|
||||
value,
|
||||
ib.byteOrder)
|
||||
}
|
||||
|
||||
err := ib.add(bt)
|
||||
|
|
|
@ -280,7 +280,7 @@ func (ie *IfdEnumerate) resolveTagValue(ite *IfdTagEntry) (valueBytes []byte, is
|
|||
|
||||
tt := NewTagType(TypeByte, ie.byteOrder)
|
||||
|
||||
if tt.ValueIsEmbedded(byteCount) == true {
|
||||
if tt.valueIsEmbedded(byteCount) == true {
|
||||
iteLogger.Debugf(nil, "Reading BYTE value (ITE; embedded).")
|
||||
|
||||
// In this case, the bytes normally used for the offset are actually
|
||||
|
|
|
@ -125,7 +125,7 @@ func (ite IfdTagEntry) ValueBytes(addressableData []byte, byteOrder binary.ByteO
|
|||
|
||||
tt := NewTagType(TypeByte, byteOrder)
|
||||
|
||||
if tt.ValueIsEmbedded(byteCount) == true {
|
||||
if tt.valueIsEmbedded(byteCount) == true {
|
||||
iteLogger.Debugf(nil, "Reading BYTE value (ITE; embedded).")
|
||||
|
||||
// In this case, the bytes normally used for the offset are actually
|
||||
|
|
283
type.go
283
type.go
|
@ -1,8 +1,8 @@
|
|||
package exif
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -13,13 +13,13 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
TypeByte = uint16(1)
|
||||
TypeAscii = uint16(2)
|
||||
TypeShort = uint16(3)
|
||||
TypeLong = uint16(4)
|
||||
TypeRational = uint16(5)
|
||||
TypeUndefined = uint16(7)
|
||||
TypeSignedLong = uint16(9)
|
||||
TypeByte = uint16(1)
|
||||
TypeAscii = uint16(2)
|
||||
TypeShort = uint16(3)
|
||||
TypeLong = uint16(4)
|
||||
TypeRational = uint16(5)
|
||||
TypeUndefined = uint16(7)
|
||||
TypeSignedLong = uint16(9)
|
||||
TypeSignedRational = uint16(10)
|
||||
|
||||
// Custom, for our purposes.
|
||||
|
@ -31,20 +31,21 @@ var (
|
|||
)
|
||||
|
||||
var (
|
||||
TypeNames = map[uint16]string {
|
||||
TypeByte: "BYTE",
|
||||
TypeAscii: "ASCII",
|
||||
TypeShort: "SHORT",
|
||||
TypeLong: "LONG",
|
||||
TypeRational: "RATIONAL",
|
||||
TypeUndefined: "UNDEFINED",
|
||||
TypeSignedLong: "SLONG",
|
||||
// TODO(dustin): Rename TypeNames() to typeNames() and add getter.
|
||||
TypeNames = map[uint16]string{
|
||||
TypeByte: "BYTE",
|
||||
TypeAscii: "ASCII",
|
||||
TypeShort: "SHORT",
|
||||
TypeLong: "LONG",
|
||||
TypeRational: "RATIONAL",
|
||||
TypeUndefined: "UNDEFINED",
|
||||
TypeSignedLong: "SLONG",
|
||||
TypeSignedRational: "SRATIONAL",
|
||||
|
||||
TypeAsciiNoNul: "_ASCII_NO_NUL",
|
||||
}
|
||||
|
||||
TypeNamesR = map[string]uint16 {}
|
||||
TypeNamesR = map[string]uint16{}
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -62,27 +63,19 @@ var (
|
|||
ErrUnhandledUnknownTypedTag = errors.New("not a standard unknown-typed tag")
|
||||
)
|
||||
|
||||
|
||||
type Rational struct {
|
||||
Numerator uint32
|
||||
Numerator uint32
|
||||
Denominator uint32
|
||||
}
|
||||
|
||||
type SignedRational struct {
|
||||
Numerator int32
|
||||
Numerator int32
|
||||
Denominator int32
|
||||
}
|
||||
|
||||
func init() {
|
||||
for typeId, typeName := range TypeNames {
|
||||
TypeNamesR[typeName] = typeId
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type TagType struct {
|
||||
tagType uint16
|
||||
name string
|
||||
tagType uint16
|
||||
name string
|
||||
byteOrder binary.ByteOrder
|
||||
}
|
||||
|
||||
|
@ -93,8 +86,8 @@ func NewTagType(tagType uint16, byteOrder binary.ByteOrder) TagType {
|
|||
}
|
||||
|
||||
return TagType{
|
||||
tagType: tagType,
|
||||
name: name,
|
||||
tagType: tagType,
|
||||
name: name,
|
||||
byteOrder: byteOrder,
|
||||
}
|
||||
}
|
||||
|
@ -142,12 +135,27 @@ func TagTypeSize(tagType uint16) int {
|
|||
}
|
||||
}
|
||||
|
||||
// ValueIsEmbedded will return a boolean indicating whether the value should be
|
||||
// valueIsEmbedded will return a boolean indicating whether the value should be
|
||||
// found directly within the IFD entry or an offset to somewhere else.
|
||||
func (tt TagType) ValueIsEmbedded(unitCount uint32) bool {
|
||||
func (tt TagType) valueIsEmbedded(unitCount uint32) bool {
|
||||
return (tt.Size() * int(unitCount)) <= 4
|
||||
}
|
||||
|
||||
func (tt TagType) readRawEncoded(valueContext ValueContext) (rawBytes []byte, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
if tt.valueIsEmbedded(valueContext.UnitCount) == true {
|
||||
byteLength := uint32(tt.Size()) * valueContext.UnitCount
|
||||
return valueContext.RawValueOffset[:byteLength], nil
|
||||
} else {
|
||||
return valueContext.AddressableData[valueContext.ValueOffset : valueContext.ValueOffset+valueContext.UnitCount*uint32(tt.Size())], nil
|
||||
}
|
||||
}
|
||||
|
||||
func (tt TagType) ParseBytes(data []byte, unitCount uint32) (value []uint8, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
|
@ -188,7 +196,7 @@ func (tt TagType) ParseAscii(data []byte, unitCount uint32) (value string, err e
|
|||
log.Panic(ErrNotEnoughData)
|
||||
}
|
||||
|
||||
if len(data) == 0 || data[count - 1] != 0 {
|
||||
if len(data) == 0 || data[count-1] != 0 {
|
||||
s := string(data[:count])
|
||||
typeLogger.Warningf(nil, "ascii not terminated with nul as expected: [%v]", s)
|
||||
|
||||
|
@ -197,7 +205,7 @@ func (tt TagType) ParseAscii(data []byte, unitCount uint32) (value string, err e
|
|||
// Auto-strip the NUL from the end. It serves no purpose outside of
|
||||
// encoding semantics.
|
||||
|
||||
return string(data[:count - 1]), nil
|
||||
return string(data[:count-1]), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,10 +310,10 @@ func (tt TagType) ParseRationals(data []byte, unitCount uint32) (value []Rationa
|
|||
for i := 0; i < count; i++ {
|
||||
if tt.byteOrder == binary.BigEndian {
|
||||
value[i].Numerator = binary.BigEndian.Uint32(data[i*8:])
|
||||
value[i].Denominator = binary.BigEndian.Uint32(data[i*8 + 4:])
|
||||
value[i].Denominator = binary.BigEndian.Uint32(data[i*8+4:])
|
||||
} else {
|
||||
value[i].Numerator = binary.LittleEndian.Uint32(data[i*8:])
|
||||
value[i].Denominator = binary.LittleEndian.Uint32(data[i*8 + 4:])
|
||||
value[i].Denominator = binary.LittleEndian.Uint32(data[i*8+4:])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -391,12 +399,16 @@ func (tt TagType) ReadByteValues(valueContext ValueContext) (value []byte, err e
|
|||
}
|
||||
}()
|
||||
|
||||
if tt.ValueIsEmbedded(valueContext.UnitCount) == true {
|
||||
if tt.valueIsEmbedded(valueContext.UnitCount) == true {
|
||||
typeLogger.Debugf(nil, "Reading BYTE value (embedded).")
|
||||
|
||||
// In this case, the bytes normally used for the offset are actually
|
||||
// data.
|
||||
value, err = tt.ParseBytes(valueContext.RawValueOffset, valueContext.UnitCount)
|
||||
|
||||
byteLength := uint32(tt.Size()) * valueContext.UnitCount
|
||||
rawValue := valueContext.RawValueOffset[:byteLength]
|
||||
|
||||
value, err = tt.ParseBytes(rawValue, valueContext.UnitCount)
|
||||
log.PanicIf(err)
|
||||
} else {
|
||||
typeLogger.Debugf(nil, "Reading BYTE value (at offset).")
|
||||
|
@ -415,10 +427,13 @@ func (tt TagType) ReadAsciiValue(valueContext ValueContext) (value string, err e
|
|||
}
|
||||
}()
|
||||
|
||||
if tt.ValueIsEmbedded(valueContext.UnitCount) == true {
|
||||
if tt.valueIsEmbedded(valueContext.UnitCount) == true {
|
||||
typeLogger.Debugf(nil, "Reading ASCII value (embedded).")
|
||||
|
||||
value, err = tt.ParseAscii(valueContext.RawValueOffset, valueContext.UnitCount)
|
||||
byteLength := uint32(tt.Size()) * valueContext.UnitCount
|
||||
rawValue := valueContext.RawValueOffset[:byteLength]
|
||||
|
||||
value, err = tt.ParseAscii(rawValue, valueContext.UnitCount)
|
||||
log.PanicIf(err)
|
||||
} else {
|
||||
typeLogger.Debugf(nil, "Reading ASCII value (at offset).")
|
||||
|
@ -437,10 +452,13 @@ func (tt TagType) ReadAsciiNoNulValue(valueContext ValueContext) (value string,
|
|||
}
|
||||
}()
|
||||
|
||||
if tt.ValueIsEmbedded(valueContext.UnitCount) == true {
|
||||
if tt.valueIsEmbedded(valueContext.UnitCount) == true {
|
||||
typeLogger.Debugf(nil, "Reading ASCII value (no-nul; embedded).")
|
||||
|
||||
value, err = tt.ParseAsciiNoNul(valueContext.RawValueOffset, valueContext.UnitCount)
|
||||
byteLength := uint32(tt.Size()) * valueContext.UnitCount
|
||||
rawValue := valueContext.RawValueOffset[:byteLength]
|
||||
|
||||
value, err = tt.ParseAsciiNoNul(rawValue, valueContext.UnitCount)
|
||||
log.PanicIf(err)
|
||||
} else {
|
||||
typeLogger.Debugf(nil, "Reading ASCII value (no-nul; at offset).")
|
||||
|
@ -459,10 +477,13 @@ func (tt TagType) ReadShortValues(valueContext ValueContext) (value []uint16, er
|
|||
}
|
||||
}()
|
||||
|
||||
if tt.ValueIsEmbedded(valueContext.UnitCount) == true {
|
||||
if tt.valueIsEmbedded(valueContext.UnitCount) == true {
|
||||
typeLogger.Debugf(nil, "Reading SHORT value (embedded).")
|
||||
|
||||
value, err = tt.ParseShorts(valueContext.RawValueOffset, valueContext.UnitCount)
|
||||
byteLength := uint32(tt.Size()) * valueContext.UnitCount
|
||||
rawValue := valueContext.RawValueOffset[:byteLength]
|
||||
|
||||
value, err = tt.ParseShorts(rawValue, valueContext.UnitCount)
|
||||
log.PanicIf(err)
|
||||
} else {
|
||||
typeLogger.Debugf(nil, "Reading SHORT value (at offset).")
|
||||
|
@ -481,10 +502,13 @@ func (tt TagType) ReadLongValues(valueContext ValueContext) (value []uint32, err
|
|||
}
|
||||
}()
|
||||
|
||||
if tt.ValueIsEmbedded(valueContext.UnitCount) == true {
|
||||
if tt.valueIsEmbedded(valueContext.UnitCount) == true {
|
||||
typeLogger.Debugf(nil, "Reading LONG value (embedded).")
|
||||
|
||||
value, err = tt.ParseLongs(valueContext.RawValueOffset, valueContext.UnitCount)
|
||||
byteLength := uint32(tt.Size()) * valueContext.UnitCount
|
||||
rawValue := valueContext.RawValueOffset[:byteLength]
|
||||
|
||||
value, err = tt.ParseLongs(rawValue, valueContext.UnitCount)
|
||||
log.PanicIf(err)
|
||||
} else {
|
||||
typeLogger.Debugf(nil, "Reading LONG value (at offset).")
|
||||
|
@ -503,10 +527,13 @@ func (tt TagType) ReadRationalValues(valueContext ValueContext) (value []Rationa
|
|||
}
|
||||
}()
|
||||
|
||||
if tt.ValueIsEmbedded(valueContext.UnitCount) == true {
|
||||
if tt.valueIsEmbedded(valueContext.UnitCount) == true {
|
||||
typeLogger.Debugf(nil, "Reading RATIONAL value (embedded).")
|
||||
|
||||
value, err = tt.ParseRationals(valueContext.RawValueOffset, valueContext.UnitCount)
|
||||
byteLength := uint32(tt.Size()) * valueContext.UnitCount
|
||||
rawValue := valueContext.RawValueOffset[:byteLength]
|
||||
|
||||
value, err = tt.ParseRationals(rawValue, valueContext.UnitCount)
|
||||
log.PanicIf(err)
|
||||
} else {
|
||||
typeLogger.Debugf(nil, "Reading RATIONAL value (at offset).")
|
||||
|
@ -525,10 +552,13 @@ func (tt TagType) ReadSignedLongValues(valueContext ValueContext) (value []int32
|
|||
}
|
||||
}()
|
||||
|
||||
if tt.ValueIsEmbedded(valueContext.UnitCount) == true {
|
||||
if tt.valueIsEmbedded(valueContext.UnitCount) == true {
|
||||
typeLogger.Debugf(nil, "Reading SLONG value (embedded).")
|
||||
|
||||
value, err = tt.ParseSignedLongs(valueContext.RawValueOffset, valueContext.UnitCount)
|
||||
byteLength := uint32(tt.Size()) * valueContext.UnitCount
|
||||
rawValue := valueContext.RawValueOffset[:byteLength]
|
||||
|
||||
value, err = tt.ParseSignedLongs(rawValue, valueContext.UnitCount)
|
||||
log.PanicIf(err)
|
||||
} else {
|
||||
typeLogger.Debugf(nil, "Reading SLONG value (at offset).")
|
||||
|
@ -547,10 +577,13 @@ func (tt TagType) ReadSignedRationalValues(valueContext ValueContext) (value []S
|
|||
}
|
||||
}()
|
||||
|
||||
if tt.ValueIsEmbedded(valueContext.UnitCount) == true {
|
||||
if tt.valueIsEmbedded(valueContext.UnitCount) == true {
|
||||
typeLogger.Debugf(nil, "Reading SRATIONAL value (embedded).")
|
||||
|
||||
value, err = tt.ParseSignedRationals(valueContext.RawValueOffset, valueContext.UnitCount)
|
||||
byteLength := uint32(tt.Size()) * valueContext.UnitCount
|
||||
rawValue := valueContext.RawValueOffset[:byteLength]
|
||||
|
||||
value, err = tt.ParseSignedRationals(rawValue, valueContext.UnitCount)
|
||||
log.PanicIf(err)
|
||||
} else {
|
||||
typeLogger.Debugf(nil, "Reading SRATIONAL value (at offset).")
|
||||
|
@ -578,100 +611,138 @@ func (tt TagType) ResolveAsString(valueContext ValueContext, justFirst bool) (va
|
|||
}
|
||||
}()
|
||||
|
||||
// TODO(dustin): Implement Resolve(), below.
|
||||
// valueRaw, err := tt.Resolve(valueContext)
|
||||
// log.PanicIf(err)
|
||||
rawBytes, err := tt.readRawEncoded(valueContext)
|
||||
log.PanicIf(err)
|
||||
|
||||
valueString, err := tt.Format(rawBytes, justFirst)
|
||||
log.PanicIf(err)
|
||||
|
||||
return valueString, nil
|
||||
}
|
||||
|
||||
// Format returns a stringified value for the given bytes. Automatically
|
||||
// calculates count based on type size.
|
||||
func (tt TagType) Format(rawBytes []byte, justFirst bool) (value string, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
// TODO(dustin): !! Add tests
|
||||
|
||||
typeId := tt.Type()
|
||||
typeSize := TagTypeSize(typeId)
|
||||
|
||||
if len(rawBytes)%typeSize != 0 {
|
||||
log.Panicf("byte-count (%d) does not align for [%s] type with a size of (%d) bytes", len(rawBytes), TypeNames[typeId], typeSize)
|
||||
}
|
||||
|
||||
// unitCount is the calculated unit-count. This should equal the original
|
||||
// value from the tag (pre-resolution).
|
||||
unitCount := uint32(len(rawBytes) / typeSize)
|
||||
|
||||
// Truncate the items if it's not bytes or a string and we just want the first.
|
||||
|
||||
valueSuffix := ""
|
||||
if justFirst == true && unitCount > 1 && typeId != TypeByte && typeId != TypeAscii && typeId != TypeAsciiNoNul {
|
||||
unitCount = 1
|
||||
valueSuffix = "..."
|
||||
}
|
||||
|
||||
if typeId == TypeByte {
|
||||
raw, err := tt.ReadByteValues(valueContext)
|
||||
items, err := tt.ParseBytes(rawBytes, unitCount)
|
||||
log.PanicIf(err)
|
||||
|
||||
if justFirst == false {
|
||||
return DumpBytesToString(raw), nil
|
||||
} else if valueContext.UnitCount > 0 {
|
||||
return fmt.Sprintf("0x%02x", raw[0]), nil
|
||||
} else {
|
||||
return "", nil
|
||||
}
|
||||
return DumpBytesToString(items), nil
|
||||
} else if typeId == TypeAscii {
|
||||
raw, err := tt.ReadAsciiValue(valueContext)
|
||||
phrase, err := tt.ParseAscii(rawBytes, unitCount)
|
||||
log.PanicIf(err)
|
||||
|
||||
return fmt.Sprintf("%s", raw), nil
|
||||
return phrase, nil
|
||||
} else if typeId == TypeAsciiNoNul {
|
||||
raw, err := tt.ReadAsciiNoNulValue(valueContext)
|
||||
phrase, err := tt.ParseAsciiNoNul(rawBytes, unitCount)
|
||||
log.PanicIf(err)
|
||||
|
||||
return fmt.Sprintf("%s", raw), nil
|
||||
return phrase, nil
|
||||
} else if typeId == TypeShort {
|
||||
raw, err := tt.ReadShortValues(valueContext)
|
||||
items, err := tt.ParseShorts(rawBytes, unitCount)
|
||||
log.PanicIf(err)
|
||||
|
||||
if justFirst == false {
|
||||
return fmt.Sprintf("%v", raw), nil
|
||||
} else if valueContext.UnitCount > 0 {
|
||||
return fmt.Sprintf("%v", raw[0]), nil
|
||||
if len(items) > 0 {
|
||||
if justFirst == true {
|
||||
return fmt.Sprintf("%v%s", items[0], valueSuffix), nil
|
||||
} else {
|
||||
return fmt.Sprintf("%v", items), nil
|
||||
}
|
||||
} else {
|
||||
return "", nil
|
||||
}
|
||||
} else if typeId == TypeLong {
|
||||
raw, err := tt.ReadLongValues(valueContext)
|
||||
items, err := tt.ParseLongs(rawBytes, unitCount)
|
||||
log.PanicIf(err)
|
||||
|
||||
if justFirst == false {
|
||||
return fmt.Sprintf("%v", raw), nil
|
||||
} else if valueContext.UnitCount > 0 {
|
||||
return fmt.Sprintf("%v", raw[0]), nil
|
||||
if len(items) > 0 {
|
||||
if justFirst == true {
|
||||
return fmt.Sprintf("%v%s", items[0], valueSuffix), nil
|
||||
} else {
|
||||
return fmt.Sprintf("%v", items), nil
|
||||
}
|
||||
} else {
|
||||
return "", nil
|
||||
}
|
||||
} else if typeId == TypeRational {
|
||||
raw, err := tt.ReadRationalValues(valueContext)
|
||||
items, err := tt.ParseRationals(rawBytes, unitCount)
|
||||
log.PanicIf(err)
|
||||
|
||||
parts := make([]string, len(raw))
|
||||
for i, r := range raw {
|
||||
parts[i] = fmt.Sprintf("%d/%d", r.Numerator, r.Denominator)
|
||||
}
|
||||
if len(items) > 0 {
|
||||
parts := make([]string, len(items))
|
||||
for i, r := range items {
|
||||
parts[i] = fmt.Sprintf("%d/%d", r.Numerator, r.Denominator)
|
||||
}
|
||||
|
||||
if justFirst == false {
|
||||
return fmt.Sprintf("%v", parts), nil
|
||||
} else if valueContext.UnitCount > 0 {
|
||||
return parts[0], nil
|
||||
if justFirst == true {
|
||||
return fmt.Sprintf("%v%s", parts[0], valueSuffix), nil
|
||||
} else {
|
||||
return fmt.Sprintf("%v", parts), nil
|
||||
}
|
||||
} else {
|
||||
return "", nil
|
||||
}
|
||||
} else if typeId == TypeSignedLong {
|
||||
raw, err := tt.ReadSignedLongValues(valueContext)
|
||||
items, err := tt.ParseSignedLongs(rawBytes, unitCount)
|
||||
log.PanicIf(err)
|
||||
|
||||
if justFirst == false {
|
||||
return fmt.Sprintf("%v", raw), nil
|
||||
} else if valueContext.UnitCount > 0 {
|
||||
return fmt.Sprintf("%v", raw[0]), nil
|
||||
if len(items) > 0 {
|
||||
if justFirst == true {
|
||||
return fmt.Sprintf("%v%s", items[0], valueSuffix), nil
|
||||
} else {
|
||||
return fmt.Sprintf("%v", items), nil
|
||||
}
|
||||
} else {
|
||||
return "", nil
|
||||
}
|
||||
} else if typeId == TypeSignedRational {
|
||||
raw, err := tt.ReadSignedRationalValues(valueContext)
|
||||
items, err := tt.ParseSignedRationals(rawBytes, unitCount)
|
||||
log.PanicIf(err)
|
||||
|
||||
parts := make([]string, len(raw))
|
||||
for i, r := range raw {
|
||||
parts := make([]string, len(items))
|
||||
for i, r := range items {
|
||||
parts[i] = fmt.Sprintf("%d/%d", r.Numerator, r.Denominator)
|
||||
}
|
||||
|
||||
if justFirst == false {
|
||||
return fmt.Sprintf("%v", raw), nil
|
||||
} else if valueContext.UnitCount > 0 {
|
||||
return parts[0], nil
|
||||
if len(items) > 0 {
|
||||
if justFirst == true {
|
||||
return fmt.Sprintf("%v%s", parts[0], valueSuffix), nil
|
||||
} else {
|
||||
return fmt.Sprintf("%v", parts), nil
|
||||
}
|
||||
} else {
|
||||
return "", nil
|
||||
}
|
||||
} else {
|
||||
log.Panicf("value of type (%d) [%s] is unparseable", typeId, tt)
|
||||
// Affects only "unknown" values, in general.
|
||||
log.Panicf("value of type (%d) [%s] can not be formatted into string", typeId, tt)
|
||||
|
||||
// Never called.
|
||||
return "", nil
|
||||
|
@ -755,7 +826,7 @@ func (tt TagType) FromString(valueString string) (value interface{}, err error)
|
|||
}()
|
||||
|
||||
if tt.tagType == TypeUndefined {
|
||||
// TODO(dustin): Circle back to this.
|
||||
// TODO(dustin): Circle back to this.
|
||||
log.Panicf("undefined-type values are not supported")
|
||||
}
|
||||
|
||||
|
@ -787,7 +858,7 @@ func (tt TagType) FromString(valueString string) (value interface{}, err error)
|
|||
log.PanicIf(err)
|
||||
|
||||
return Rational{
|
||||
Numerator: uint32(numerator),
|
||||
Numerator: uint32(numerator),
|
||||
Denominator: uint32(denominator),
|
||||
}, nil
|
||||
} else if tt.tagType == TypeSignedLong {
|
||||
|
@ -805,7 +876,7 @@ func (tt TagType) FromString(valueString string) (value interface{}, err error)
|
|||
log.PanicIf(err)
|
||||
|
||||
return SignedRational{
|
||||
Numerator: int32(numerator),
|
||||
Numerator: int32(numerator),
|
||||
Denominator: int32(denominator),
|
||||
}, nil
|
||||
}
|
||||
|
@ -813,3 +884,9 @@ func (tt TagType) FromString(valueString string) (value interface{}, err error)
|
|||
log.Panicf("from-string encoding for type not supported; this shouldn't happen: (%d)", tt.Type())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
for typeId, typeName := range TypeNames {
|
||||
TypeNamesR[typeName] = typeId
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue