-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathproperty.go
207 lines (192 loc) · 5.9 KB
/
property.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
package aep
import (
"bytes"
"encoding/binary"
"fmt"
"strings"
"github.com/rioam2/rifx"
)
// PropertyTypeName enumerates the value/type of a property
type PropertyTypeName uint16
const (
// PropertyTypeBoolean denotes a boolean checkbox property
PropertyTypeBoolean PropertyTypeName = 0x04
// PropertyTypeOneD denotes a one-dimensional slider property
PropertyTypeOneD PropertyTypeName = 0x02
// PropertyTypeTwoD denotes a two-dimensional point property
PropertyTypeTwoD PropertyTypeName = 0x06
// PropertyTypeThreeD denotes a three-dimensional point property
PropertyTypeThreeD PropertyTypeName = 0x12
// PropertyTypeColor denotes a four-dimensional color property
PropertyTypeColor PropertyTypeName = 0x05
// PropertyTypeAngle denotes a one-dimensional angle property
PropertyTypeAngle PropertyTypeName = 0x03
// PropertyTypeLayerSelect denotes a single-valued layer selection property
PropertyTypeLayerSelect PropertyTypeName = 0x00
// PropertyTypeSelect denotes a single-valued selection property
PropertyTypeSelect PropertyTypeName = 0x07
// PropertyTypeGroup denotes a collection/group property
PropertyTypeGroup PropertyTypeName = 0x0d
// PropertyTypeCustom denotes an unknown/custom property type (default)
PropertyTypeCustom PropertyTypeName = 0x0f
)
// String translates a property type enumeration to string
func (p PropertyTypeName) String() string {
switch p {
case PropertyTypeBoolean:
return "Boolean"
case PropertyTypeOneD:
return "OneD"
case PropertyTypeTwoD:
return "TwoD"
case PropertyTypeThreeD:
return "ThreeD"
case PropertyTypeColor:
return "Color"
case PropertyTypeAngle:
return "Angle"
case PropertyTypeLayerSelect:
return "LayerSelect"
case PropertyTypeSelect:
return "Select"
case PropertyTypeGroup:
return "Group"
default:
return "Custom"
}
}
// Property describes a property object of a layer or nested property
type Property struct {
MatchName string
Name string
Label string
Index uint32
PropertyType PropertyTypeName
Properties []*Property
SelectOptions []string
}
func parseProperty(propData interface{}, matchName string) (*Property, error) {
prop := &Property{}
// Apply some sensible default values
prop.PropertyType = PropertyTypeCustom
prop.SelectOptions = make([]string, 0)
prop.MatchName = matchName
prop.Name = matchName
switch matchName {
case "ADBE Effect Parade":
prop.Name = "Effects"
}
// Handle different types of property data
switch propData.(type) {
case *rifx.List:
propHead := propData.(*rifx.List)
// Parse sub-properties
prop.Properties = make([]*Property, 0)
tdgpMap, orderedMatchNames := indexedGroupToMap(propHead)
for idx, mn := range orderedMatchNames {
subProp, err := parseProperty(tdgpMap[mn], mn)
if err == nil {
subProp.Index = uint32(idx) + 1
prop.Properties = append(prop.Properties, subProp)
}
}
// Parse effect sub-properties
if propHead.Identifier == "sspc" {
prop.PropertyType = PropertyTypeGroup
fnamBlock, err := propHead.FindByType("fnam")
if err == nil {
prop.Name = fnamBlock.ToString()
}
tdgpBlock, err := propHead.SublistFind("tdgp")
if err == nil {
// Look for a tdsn which specifies the user-defined label of the property
tdsnBlock, err := tdgpBlock.FindByType("tdsn")
if err == nil {
label := fmt.Sprintf("%s", tdsnBlock.ToString())
// Check if there is a custom user defined label added. The default if there
// is not is "-_0_/-" for some unknown reason.
if label != "-_0_/-" {
prop.Label = label
}
}
}
parTList := propHead.SublistMerge("parT")
subPropMatchNames, subPropPards := pairMatchNames(parTList)
for idx, mn := range subPropMatchNames {
// Skip first pard entry (describes parent)
if idx == 0 {
continue
}
subProp, err := parseProperty(subPropPards[idx], mn)
if err == nil {
subProp.Index = uint32(idx)
prop.Properties = append(prop.Properties, subProp)
}
}
}
case []interface{}:
for _, entry := range propData.([]interface{}) {
if block, ok := entry.(*rifx.Block); ok {
switch block.Type {
case "pdnm":
strContent := block.ToString()
if prop.PropertyType == PropertyTypeSelect {
prop.SelectOptions = strings.Split(strContent, "|")
} else if strContent != "" {
prop.Name = strContent
}
case "pard":
blockData := block.Data.([]byte)
prop.PropertyType = PropertyTypeName(binary.BigEndian.Uint16(blockData[14:16]))
if prop.PropertyType == 0x0a {
prop.PropertyType = PropertyTypeOneD
}
pardName := fmt.Sprintf("%s", bytes.Trim(blockData[16:48], "\x00"))
if pardName != "" {
prop.Name = pardName
}
}
}
}
}
return prop, nil
}
func pairMatchNames(head *rifx.List) ([]string, [][]interface{}) {
matchNames := make([]string, 0)
datum := make([][]interface{}, 0)
if head != nil {
groupIdx := -1
skipToNextTDMNFlag := false
for _, block := range head.Blocks {
if block.Type == "tdmn" {
matchName := fmt.Sprintf("%s", bytes.Trim(block.Data.([]byte), "\x00"))
if matchName == "ADBE Group End" || matchName == "ADBE Effect Built In Params" {
skipToNextTDMNFlag = true
continue
}
matchNames = append(matchNames, matchName)
skipToNextTDMNFlag = false
groupIdx++
} else if groupIdx >= 0 && !skipToNextTDMNFlag {
if groupIdx >= len(datum) {
datum = append(datum, make([]interface{}, 0))
}
switch block.Data.(type) {
case *rifx.List:
datum[groupIdx] = append(datum[groupIdx], block.Data)
default:
datum[groupIdx] = append(datum[groupIdx], block)
}
}
}
}
return matchNames, datum
}
func indexedGroupToMap(tdgpHead *rifx.List) (map[string]*rifx.List, []string) {
tdgpMap := make(map[string]*rifx.List, 0)
matchNames, contents := pairMatchNames(tdgpHead)
for idx, matchName := range matchNames {
tdgpMap[matchName] = contents[idx][0].(*rifx.List)
}
return tdgpMap, matchNames
}