mirror of
https://github.com/dsoprea/go-exif.git
synced 2025-05-31 11:41:57 +00:00
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.
This commit is contained in:
parent
636417e4ae
commit
483dbacf14
@ -593,8 +593,8 @@
|
|||||||
"tag_type_id": 5,
|
"tag_type_id": 5,
|
||||||
"tag_type_name": "RATIONAL",
|
"tag_type_name": "RATIONAL",
|
||||||
"unit_count": 4,
|
"unit_count": 4,
|
||||||
"value": "16/1",
|
"value": "16/1...",
|
||||||
"value_string": "16/1"
|
"value_string": "16/1..."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ifd_path": "IFD/Exif",
|
"ifd_path": "IFD/Exif",
|
||||||
@ -641,8 +641,8 @@
|
|||||||
"tag_type_id": 1,
|
"tag_type_id": 1,
|
||||||
"tag_type_name": "BYTE",
|
"tag_type_name": "BYTE",
|
||||||
"unit_count": 4,
|
"unit_count": 4,
|
||||||
"value": "0x02",
|
"value": "02 03 00 00",
|
||||||
"value_string": "0x02"
|
"value_string": "02 03 00 00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ifd_path": "IFD",
|
"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=(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=(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=(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=(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/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] 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=(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=(0x011a) NAME=[XResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]
|
||||||
IFD-PATH=[IFD] ID=(0x011b) NAME=[YResolution] 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=(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=(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=(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=(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/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] 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=(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=(0x011a) NAME=[XResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]",
|
||||||
"IFD-PATH=[IFD] ID=(0x011b) NAME=[YResolution] 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 {
|
func (ibtv IfdBuilderTagValue) IsBytes() bool {
|
||||||
return ibtv.valueBytes != nil
|
return ibtv.valueBytes != nil
|
||||||
}
|
}
|
||||||
@ -94,14 +96,19 @@ type BuilderTag struct {
|
|||||||
// child IFDs), or an IfdTagEntry instance representing an existing,
|
// child IFDs), or an IfdTagEntry instance representing an existing,
|
||||||
// previously-stored tag.
|
// previously-stored tag.
|
||||||
value *IfdBuilderTagValue
|
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{
|
return &BuilderTag{
|
||||||
ifdPath: ifdPath,
|
ifdPath: ifdPath,
|
||||||
tagId: tagId,
|
tagId: tagId,
|
||||||
typeId: typeId,
|
typeId: typeId,
|
||||||
value: value,
|
value: value,
|
||||||
|
byteOrder: byteOrder,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +126,20 @@ func (bt *BuilderTag) Value() (value *IfdBuilderTagValue) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bt *BuilderTag) String() string {
|
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) {
|
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,
|
ifdPath,
|
||||||
it.Id,
|
it.Id,
|
||||||
typeId,
|
typeId,
|
||||||
tagValue)
|
tagValue,
|
||||||
|
byteOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
type IfdBuilder struct {
|
type IfdBuilder struct {
|
||||||
@ -513,7 +534,13 @@ func (ib *IfdBuilder) SetThumbnail(data []byte) (err error) {
|
|||||||
ib.thumbnailData = data
|
ib.thumbnailData = data
|
||||||
|
|
||||||
ibtvfb := NewIfdBuilderTagValueFromBytes(ib.thumbnailData)
|
ibtvfb := NewIfdBuilderTagValueFromBytes(ib.thumbnailData)
|
||||||
offsetBt := NewBuilderTag(ib.ifdPath, ThumbnailOffsetTagId, TypeLong, ibtvfb)
|
offsetBt :=
|
||||||
|
NewBuilderTag(
|
||||||
|
ib.ifdPath,
|
||||||
|
ThumbnailOffsetTagId,
|
||||||
|
TypeLong,
|
||||||
|
ibtvfb,
|
||||||
|
ib.byteOrder)
|
||||||
|
|
||||||
err = ib.Set(offsetBt)
|
err = ib.Set(offsetBt)
|
||||||
log.PanicIf(err)
|
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
|
// (nor does it matter since this is just a temporary
|
||||||
// placeholder, in this situation).
|
// placeholder, in this situation).
|
||||||
value := NewIfdBuilderTagValueFromBytes([]byte{0, 0, 0, 0})
|
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 {
|
} else {
|
||||||
// Figure out which of the child-IFDs that are associated with
|
// Figure out which of the child-IFDs that are associated with
|
||||||
// this IFD represents this specific child IFD.
|
// this IFD represents this specific child IFD.
|
||||||
@ -1107,7 +1139,12 @@ func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, itevr *IfdTagEntryValueResol
|
|||||||
value = NewIfdBuilderTagValueFromBytes(valueBytes)
|
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)
|
err := ib.add(bt)
|
||||||
|
@ -280,7 +280,7 @@ func (ie *IfdEnumerate) resolveTagValue(ite *IfdTagEntry) (valueBytes []byte, is
|
|||||||
|
|
||||||
tt := NewTagType(TypeByte, ie.byteOrder)
|
tt := NewTagType(TypeByte, ie.byteOrder)
|
||||||
|
|
||||||
if tt.ValueIsEmbedded(byteCount) == true {
|
if tt.valueIsEmbedded(byteCount) == true {
|
||||||
iteLogger.Debugf(nil, "Reading BYTE value (ITE; embedded).")
|
iteLogger.Debugf(nil, "Reading BYTE value (ITE; embedded).")
|
||||||
|
|
||||||
// In this case, the bytes normally used for the offset are actually
|
// 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)
|
tt := NewTagType(TypeByte, byteOrder)
|
||||||
|
|
||||||
if tt.ValueIsEmbedded(byteCount) == true {
|
if tt.valueIsEmbedded(byteCount) == true {
|
||||||
iteLogger.Debugf(nil, "Reading BYTE value (ITE; embedded).")
|
iteLogger.Debugf(nil, "Reading BYTE value (ITE; embedded).")
|
||||||
|
|
||||||
// In this case, the bytes normally used for the offset are actually
|
// In this case, the bytes normally used for the offset are actually
|
||||||
|
283
type.go
283
type.go
@ -1,8 +1,8 @@
|
|||||||
package exif
|
package exif
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -13,13 +13,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TypeByte = uint16(1)
|
TypeByte = uint16(1)
|
||||||
TypeAscii = uint16(2)
|
TypeAscii = uint16(2)
|
||||||
TypeShort = uint16(3)
|
TypeShort = uint16(3)
|
||||||
TypeLong = uint16(4)
|
TypeLong = uint16(4)
|
||||||
TypeRational = uint16(5)
|
TypeRational = uint16(5)
|
||||||
TypeUndefined = uint16(7)
|
TypeUndefined = uint16(7)
|
||||||
TypeSignedLong = uint16(9)
|
TypeSignedLong = uint16(9)
|
||||||
TypeSignedRational = uint16(10)
|
TypeSignedRational = uint16(10)
|
||||||
|
|
||||||
// Custom, for our purposes.
|
// Custom, for our purposes.
|
||||||
@ -31,20 +31,21 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TypeNames = map[uint16]string {
|
// TODO(dustin): Rename TypeNames() to typeNames() and add getter.
|
||||||
TypeByte: "BYTE",
|
TypeNames = map[uint16]string{
|
||||||
TypeAscii: "ASCII",
|
TypeByte: "BYTE",
|
||||||
TypeShort: "SHORT",
|
TypeAscii: "ASCII",
|
||||||
TypeLong: "LONG",
|
TypeShort: "SHORT",
|
||||||
TypeRational: "RATIONAL",
|
TypeLong: "LONG",
|
||||||
TypeUndefined: "UNDEFINED",
|
TypeRational: "RATIONAL",
|
||||||
TypeSignedLong: "SLONG",
|
TypeUndefined: "UNDEFINED",
|
||||||
|
TypeSignedLong: "SLONG",
|
||||||
TypeSignedRational: "SRATIONAL",
|
TypeSignedRational: "SRATIONAL",
|
||||||
|
|
||||||
TypeAsciiNoNul: "_ASCII_NO_NUL",
|
TypeAsciiNoNul: "_ASCII_NO_NUL",
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeNamesR = map[string]uint16 {}
|
TypeNamesR = map[string]uint16{}
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -62,27 +63,19 @@ var (
|
|||||||
ErrUnhandledUnknownTypedTag = errors.New("not a standard unknown-typed tag")
|
ErrUnhandledUnknownTypedTag = errors.New("not a standard unknown-typed tag")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
type Rational struct {
|
type Rational struct {
|
||||||
Numerator uint32
|
Numerator uint32
|
||||||
Denominator uint32
|
Denominator uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
type SignedRational struct {
|
type SignedRational struct {
|
||||||
Numerator int32
|
Numerator int32
|
||||||
Denominator int32
|
Denominator int32
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
|
||||||
for typeId, typeName := range TypeNames {
|
|
||||||
TypeNamesR[typeName] = typeId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type TagType struct {
|
type TagType struct {
|
||||||
tagType uint16
|
tagType uint16
|
||||||
name string
|
name string
|
||||||
byteOrder binary.ByteOrder
|
byteOrder binary.ByteOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,8 +86,8 @@ func NewTagType(tagType uint16, byteOrder binary.ByteOrder) TagType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return TagType{
|
return TagType{
|
||||||
tagType: tagType,
|
tagType: tagType,
|
||||||
name: name,
|
name: name,
|
||||||
byteOrder: byteOrder,
|
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.
|
// 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
|
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) {
|
func (tt TagType) ParseBytes(data []byte, unitCount uint32) (value []uint8, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if state := recover(); state != nil {
|
if state := recover(); state != nil {
|
||||||
@ -188,7 +196,7 @@ func (tt TagType) ParseAscii(data []byte, unitCount uint32) (value string, err e
|
|||||||
log.Panic(ErrNotEnoughData)
|
log.Panic(ErrNotEnoughData)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data) == 0 || data[count - 1] != 0 {
|
if len(data) == 0 || data[count-1] != 0 {
|
||||||
s := string(data[:count])
|
s := string(data[:count])
|
||||||
typeLogger.Warningf(nil, "ascii not terminated with nul as expected: [%v]", s)
|
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
|
// Auto-strip the NUL from the end. It serves no purpose outside of
|
||||||
// encoding semantics.
|
// 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++ {
|
for i := 0; i < count; i++ {
|
||||||
if tt.byteOrder == binary.BigEndian {
|
if tt.byteOrder == binary.BigEndian {
|
||||||
value[i].Numerator = binary.BigEndian.Uint32(data[i*8:])
|
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 {
|
} else {
|
||||||
value[i].Numerator = binary.LittleEndian.Uint32(data[i*8:])
|
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).")
|
typeLogger.Debugf(nil, "Reading BYTE value (embedded).")
|
||||||
|
|
||||||
// In this case, the bytes normally used for the offset are actually
|
// In this case, the bytes normally used for the offset are actually
|
||||||
// data.
|
// 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)
|
log.PanicIf(err)
|
||||||
} else {
|
} else {
|
||||||
typeLogger.Debugf(nil, "Reading BYTE value (at offset).")
|
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).")
|
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)
|
log.PanicIf(err)
|
||||||
} else {
|
} else {
|
||||||
typeLogger.Debugf(nil, "Reading ASCII value (at offset).")
|
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).")
|
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)
|
log.PanicIf(err)
|
||||||
} else {
|
} else {
|
||||||
typeLogger.Debugf(nil, "Reading ASCII value (no-nul; at offset).")
|
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).")
|
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)
|
log.PanicIf(err)
|
||||||
} else {
|
} else {
|
||||||
typeLogger.Debugf(nil, "Reading SHORT value (at offset).")
|
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).")
|
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)
|
log.PanicIf(err)
|
||||||
} else {
|
} else {
|
||||||
typeLogger.Debugf(nil, "Reading LONG value (at offset).")
|
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).")
|
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)
|
log.PanicIf(err)
|
||||||
} else {
|
} else {
|
||||||
typeLogger.Debugf(nil, "Reading RATIONAL value (at offset).")
|
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).")
|
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)
|
log.PanicIf(err)
|
||||||
} else {
|
} else {
|
||||||
typeLogger.Debugf(nil, "Reading SLONG value (at offset).")
|
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).")
|
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)
|
log.PanicIf(err)
|
||||||
} else {
|
} else {
|
||||||
typeLogger.Debugf(nil, "Reading SRATIONAL value (at offset).")
|
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.
|
rawBytes, err := tt.readRawEncoded(valueContext)
|
||||||
// valueRaw, err := tt.Resolve(valueContext)
|
log.PanicIf(err)
|
||||||
// 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()
|
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 {
|
if typeId == TypeByte {
|
||||||
raw, err := tt.ReadByteValues(valueContext)
|
items, err := tt.ParseBytes(rawBytes, unitCount)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if justFirst == false {
|
return DumpBytesToString(items), nil
|
||||||
return DumpBytesToString(raw), nil
|
|
||||||
} else if valueContext.UnitCount > 0 {
|
|
||||||
return fmt.Sprintf("0x%02x", raw[0]), nil
|
|
||||||
} else {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
} else if typeId == TypeAscii {
|
} else if typeId == TypeAscii {
|
||||||
raw, err := tt.ReadAsciiValue(valueContext)
|
phrase, err := tt.ParseAscii(rawBytes, unitCount)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
return fmt.Sprintf("%s", raw), nil
|
return phrase, nil
|
||||||
} else if typeId == TypeAsciiNoNul {
|
} else if typeId == TypeAsciiNoNul {
|
||||||
raw, err := tt.ReadAsciiNoNulValue(valueContext)
|
phrase, err := tt.ParseAsciiNoNul(rawBytes, unitCount)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
return fmt.Sprintf("%s", raw), nil
|
return phrase, nil
|
||||||
} else if typeId == TypeShort {
|
} else if typeId == TypeShort {
|
||||||
raw, err := tt.ReadShortValues(valueContext)
|
items, err := tt.ParseShorts(rawBytes, unitCount)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if justFirst == false {
|
if len(items) > 0 {
|
||||||
return fmt.Sprintf("%v", raw), nil
|
if justFirst == true {
|
||||||
} else if valueContext.UnitCount > 0 {
|
return fmt.Sprintf("%v%s", items[0], valueSuffix), nil
|
||||||
return fmt.Sprintf("%v", raw[0]), nil
|
} else {
|
||||||
|
return fmt.Sprintf("%v", items), nil
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
} else if typeId == TypeLong {
|
} else if typeId == TypeLong {
|
||||||
raw, err := tt.ReadLongValues(valueContext)
|
items, err := tt.ParseLongs(rawBytes, unitCount)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if justFirst == false {
|
if len(items) > 0 {
|
||||||
return fmt.Sprintf("%v", raw), nil
|
if justFirst == true {
|
||||||
} else if valueContext.UnitCount > 0 {
|
return fmt.Sprintf("%v%s", items[0], valueSuffix), nil
|
||||||
return fmt.Sprintf("%v", raw[0]), nil
|
} else {
|
||||||
|
return fmt.Sprintf("%v", items), nil
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
} else if typeId == TypeRational {
|
} else if typeId == TypeRational {
|
||||||
raw, err := tt.ReadRationalValues(valueContext)
|
items, err := tt.ParseRationals(rawBytes, unitCount)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
parts := make([]string, len(raw))
|
if len(items) > 0 {
|
||||||
for i, r := range raw {
|
parts := make([]string, len(items))
|
||||||
parts[i] = fmt.Sprintf("%d/%d", r.Numerator, r.Denominator)
|
for i, r := range items {
|
||||||
}
|
parts[i] = fmt.Sprintf("%d/%d", r.Numerator, r.Denominator)
|
||||||
|
}
|
||||||
|
|
||||||
if justFirst == false {
|
if justFirst == true {
|
||||||
return fmt.Sprintf("%v", parts), nil
|
return fmt.Sprintf("%v%s", parts[0], valueSuffix), nil
|
||||||
} else if valueContext.UnitCount > 0 {
|
} else {
|
||||||
return parts[0], nil
|
return fmt.Sprintf("%v", parts), nil
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
} else if typeId == TypeSignedLong {
|
} else if typeId == TypeSignedLong {
|
||||||
raw, err := tt.ReadSignedLongValues(valueContext)
|
items, err := tt.ParseSignedLongs(rawBytes, unitCount)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
if justFirst == false {
|
if len(items) > 0 {
|
||||||
return fmt.Sprintf("%v", raw), nil
|
if justFirst == true {
|
||||||
} else if valueContext.UnitCount > 0 {
|
return fmt.Sprintf("%v%s", items[0], valueSuffix), nil
|
||||||
return fmt.Sprintf("%v", raw[0]), nil
|
} else {
|
||||||
|
return fmt.Sprintf("%v", items), nil
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
} else if typeId == TypeSignedRational {
|
} else if typeId == TypeSignedRational {
|
||||||
raw, err := tt.ReadSignedRationalValues(valueContext)
|
items, err := tt.ParseSignedRationals(rawBytes, unitCount)
|
||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
parts := make([]string, len(raw))
|
parts := make([]string, len(items))
|
||||||
for i, r := range raw {
|
for i, r := range items {
|
||||||
parts[i] = fmt.Sprintf("%d/%d", r.Numerator, r.Denominator)
|
parts[i] = fmt.Sprintf("%d/%d", r.Numerator, r.Denominator)
|
||||||
}
|
}
|
||||||
|
|
||||||
if justFirst == false {
|
if len(items) > 0 {
|
||||||
return fmt.Sprintf("%v", raw), nil
|
if justFirst == true {
|
||||||
} else if valueContext.UnitCount > 0 {
|
return fmt.Sprintf("%v%s", parts[0], valueSuffix), nil
|
||||||
return parts[0], nil
|
} else {
|
||||||
|
return fmt.Sprintf("%v", parts), nil
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
} else {
|
} 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.
|
// Never called.
|
||||||
return "", nil
|
return "", nil
|
||||||
@ -755,7 +826,7 @@ func (tt TagType) FromString(valueString string) (value interface{}, err error)
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
if tt.tagType == TypeUndefined {
|
if tt.tagType == TypeUndefined {
|
||||||
// TODO(dustin): Circle back to this.
|
// TODO(dustin): Circle back to this.
|
||||||
log.Panicf("undefined-type values are not supported")
|
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)
|
log.PanicIf(err)
|
||||||
|
|
||||||
return Rational{
|
return Rational{
|
||||||
Numerator: uint32(numerator),
|
Numerator: uint32(numerator),
|
||||||
Denominator: uint32(denominator),
|
Denominator: uint32(denominator),
|
||||||
}, nil
|
}, nil
|
||||||
} else if tt.tagType == TypeSignedLong {
|
} else if tt.tagType == TypeSignedLong {
|
||||||
@ -805,7 +876,7 @@ func (tt TagType) FromString(valueString string) (value interface{}, err error)
|
|||||||
log.PanicIf(err)
|
log.PanicIf(err)
|
||||||
|
|
||||||
return SignedRational{
|
return SignedRational{
|
||||||
Numerator: int32(numerator),
|
Numerator: int32(numerator),
|
||||||
Denominator: int32(denominator),
|
Denominator: int32(denominator),
|
||||||
}, nil
|
}, 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())
|
log.Panicf("from-string encoding for type not supported; this shouldn't happen: (%d)", tt.Type())
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for typeId, typeName := range TypeNames {
|
||||||
|
TypeNamesR[typeName] = typeId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user