-
Notifications
You must be signed in to change notification settings - Fork 7
/
item.go
176 lines (162 loc) · 5.37 KB
/
item.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
package aep
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/rioam2/rifx"
)
// ItemTypeName denotes the type of item. See: http://docs.aenhancers.com/items/item/#item-ItemType
type ItemTypeName string
const (
// ItemTypeFolder denotes a Folder item which may contain additional items
ItemTypeFolder ItemTypeName = "Folder"
// ItemTypeComposition denotes a Composition item which has a dimension, length, framerate and child layers
ItemTypeComposition ItemTypeName = "Composition"
// ItemTypeFootage denotes an AVItem that has a source (eg: an image or video file)
ItemTypeFootage ItemTypeName = "Footage"
)
// FootageTypeName denotes the type of footage of an AVItem (eg: Solid, Placeholder, ...)
type FootageTypeName uint16
const (
// FootageTypeSolid denotes a Solid source
FootageTypeSolid FootageTypeName = 0x09
// FootageTypePlaceholder denotes a Placeholder source
FootageTypePlaceholder FootageTypeName = 0x02
)
// Item is a generalized object storing information about folders, compositions, or footage
type Item struct {
Name string
ID uint32
ItemType ItemTypeName
FolderContents []*Item
FootageDimensions [2]uint16
FootageFramerate float64
FootageSeconds float64
FootageType FootageTypeName
BackgroundColor [3]byte
CompositionLayers []*Layer
}
func parseItem(itemHead *rifx.List, project *Project) (*Item, error) {
item := &Item{}
isRoot := itemHead.Identifier == "Fold"
// Parse item metadata
if isRoot {
item.ID = 0
item.Name = "root"
item.ItemType = ItemTypeFolder
} else {
nameBlock, err := itemHead.FindByType("Utf8")
if err != nil {
return nil, err
}
item.Name = nameBlock.ToString()
type IDTA struct {
Type uint16
Unknown00 [14]byte
ID uint32
}
itemDescriptor := &IDTA{}
idtaBlock, err := itemHead.FindByType("idta")
if err != nil {
return nil, err
}
err = idtaBlock.ToStruct(itemDescriptor)
if err != nil {
return nil, err
}
item.ID = itemDescriptor.ID
switch itemDescriptor.Type {
case 0x01:
item.ItemType = ItemTypeFolder
case 0x04:
item.ItemType = ItemTypeComposition
case 0x07:
item.ItemType = ItemTypeFootage
}
}
// Parse unique item type information
switch item.ItemType {
case ItemTypeFolder:
childItemLists := append(itemHead.SublistFilter("Item"), itemHead.SublistMerge("Sfdr").SublistFilter("Item")...)
for _, childItemList := range childItemLists {
childItem, err := parseItem(childItemList, project)
if err != nil {
return nil, err
}
item.FolderContents = append(item.FolderContents, childItem)
}
case ItemTypeFootage:
pinList, err := itemHead.SublistFind("Pin ")
if err != nil {
return nil, err
}
sspcBlock, err := pinList.FindByType("sspc")
if err != nil {
return nil, err
}
type SSPC struct {
Unknown00 [30]byte // Offset 0B
Width uint32 // Offset 30B
Height uint32 // Offset 34B
SecondsDividend uint32 // Offset 38B
SecondsDivisor uint32 // Offset 42B
Unknown01 [10]byte // Offset 46B
Framerate uint32 // Offset 56B
FramerateDividend uint16 // Offset 60B
}
sspcDesc := &SSPC{}
sspcBlock.ToStruct(sspcDesc)
item.FootageDimensions = [2]uint16{uint16(sspcDesc.Width), uint16(sspcDesc.Height)}
item.FootageFramerate = float64(sspcDesc.Framerate) + (float64(sspcDesc.FramerateDividend) / float64(1<<16))
item.FootageSeconds = float64(sspcDesc.SecondsDividend) / float64(sspcDesc.SecondsDivisor)
optiBlock, err := pinList.FindByType("opti")
if err != nil {
return nil, err
}
optiData := optiBlock.Data.([]byte)
item.FootageType = FootageTypeName(binary.BigEndian.Uint16(optiData[4:6]))
switch item.FootageType {
case FootageTypeSolid:
item.Name = fmt.Sprintf("%s", bytes.ReplaceAll(bytes.Trim(optiData[26:255], "\x00"), []byte{0}, []byte{32}))
case FootageTypePlaceholder:
item.Name = fmt.Sprintf("%s", bytes.ReplaceAll(bytes.Trim(optiData[10:], "\x00"), []byte{0}, []byte{32}))
}
case ItemTypeComposition:
type CDTA struct {
Unknown00 [4]byte // Offset 0B
FramerateDivisor uint32 // Offset 4B
FramerateDividend uint32 // Offset 8B
Unknown01 [32]byte // Offset 12B
SecondsDividend uint32 // Offset 40B
SecondsDivisor uint32 // Offset 44B
BackgroundColor [3]byte // Offset 48B
Unknown03 [85]byte // Offset 51B
Width uint16 // Offset 136B
Height uint16 // Offset 138B
Unknown04 [12]byte // Offset 140B
Framerate uint16 // Offset 152B
}
compDesc := &CDTA{}
cdataBlock, err := itemHead.FindByType("cdta")
if err != nil {
return nil, err
}
cdataBlock.ToStruct(compDesc)
item.FootageDimensions = [2]uint16{compDesc.Width, compDesc.Height}
item.FootageFramerate = float64(compDesc.FramerateDividend) / float64(compDesc.FramerateDivisor)
item.FootageSeconds = float64(compDesc.SecondsDividend) / float64(compDesc.SecondsDivisor)
item.BackgroundColor = compDesc.BackgroundColor
// Parse composition's layers
for index, layerListHead := range itemHead.SublistFilter("Layr") {
layer, err := parseLayer(layerListHead, project)
if err != nil {
return nil, err
}
layer.Index = uint32(index + 1)
item.CompositionLayers = append(item.CompositionLayers, layer)
}
}
// Insert item into project items map
project.Items[item.ID] = item
return item, nil
}