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
|
# Simulation and abstraction of a disk-based file.
# Provides methods to read the raw binary file data, which is stored in a
# variable instead of read from disk. A lot of these functions are from C,
# but some of them are helper functions to make things a bit easier to
# understand.
class PSDFile
unicodeRegex: /\\u([\d\w]{4})/gi
constructor: (@data) ->
# Track our current position in the file data. This is analogous to the
# file pointer in C.
@pos = 0
# Get the current position in the file. This is here to parallel the C method
tell: -> @pos
# Read one or more bytes from the file. Note that this moves the file pointer
# forward the number of bytes specified.
read: (bytes) -> (@data[@pos++] for i in [0...bytes])
# Move the file pointer to a new position. By default, this is done relative
# to the current file pointer position. Setting the 2nd argument to false
# causes the file pointer to move to the absolute location specified.
seek: (amount, rel = true) ->
if rel then @pos += amount else @pos = amount
#
# Helper functions so we don't have to remember the unpack
# format codes.
#
# 4 bytes / 32-bit
readInt: ->
int = @readUInt()
if int >= 0x80000000 then int - 0x100000000 else int
readUInt: ->
b1 = @read(1)[0] << 24
b2 = @read(1)[0] << 16
b3 = @read(1)[0] << 8
b4 = @read(1)[0]
b1 | b2 | b3 | b4
# 2 bytes
readShortInt: ->
int = @readShortUInt()
if int >= 0x8000 then int - 0x10000 else int
readShortUInt: ->
b1 = @read(1)[0] << 8
b2 = @read(1)[0]
b1 | b2
# 4 bytes
readLongInt: -> @readf(">l")[0]
readLongUInt: -> @readf(">L")[0]
# 8 bytes
readDouble: -> @readf(">d")[0]
# 1 byte
readBoolean: -> @read(1)[0] isnt 0
# Unfortunately Javascript does not support 64-bit integers, so we
# have a temporary solution for now. In the future, we can parse and
# store the int either as an octet string, or something more useful.
readLongLong: -> @read(8)
readULongLong: -> @read(8)
# Reads a string with the given length. Because some strings are also
# null-byte padded, we strip out these null bytes since they are of no
# use to us in Javascript.
readString: (length) ->
ret = String.fromCharCode.apply null, @read(length)
ret.replace /\u0000/g, ""
readUnicodeString: ->
len = @readInt() * 2
str = @readf(">#{len}s")[0]
str = str.replace @unicodeRegex, (match, grp) ->
String.fromCharCode parseInt(grp, 16)
str.replace /\u0000/g, ""
# Used for reading pascal strings, which are strings that have their length
# prepended to the chunk of character bytes. If a length isn't found, a
# string with the default length will be read instead.
readLengthWithString: (defaultLen = 4) ->
length = @read(1)[0]
if length is 0
str = @readString defaultLen
else
str = @readString length
str
# Reads a byte list
readBytesList: (size) -> @read size
readSpaceColor: ->
colorSpace = @readShortInt()
colorComponent = []
colorComponent.push @readShortInt() >> 8 for i in [0...4]
PSDColor.colorSpaceToARGB(colorSpace, colorComponent)
# Reads from the file given the unpack format string. Format string codes
# can be easily referenced
# [from the Python docs](http://docs.python.org/library/struct.html#format-characters)
readf: (format) -> jspack.Unpack format, @read(jspack.CalcLength(format))
# Skips a block, assuming the next byte describes the size of the section.
# An optional description is given to explain why we are skipping this block
# instead of parsing it.
skipBlock: (desc = "unknown") ->
[n] = @readf('>L')
@seek(n) if n # relative
Log.debug "Skipped #{desc} with #{n} bytes"
|