forked from difcareer/010templates
-
Notifications
You must be signed in to change notification settings - Fork 1
/
PNGTemplate.bt
executable file
·138 lines (120 loc) · 4.31 KB
/
PNGTemplate.bt
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
//--------------------------------------
//--- 010 Editor v2.0.1 Binary Template
//
// File: PNGTemplate.bt
// Author: Kevin O. Grover <[email protected]>
// Purpose: A template for parsing PNG (Portable Network Graphics) files.
// Version: 1.1 2009-02-25
// History:
// 2005-05-11 KOG Initial version
// 2005-06-29 KOG Fixed typos in comments
// 2009-02-23 KOG Decode IHDR and tEXt chunks
//
// This template was written to the PNG 1.2 Specification.
// Note however, that it does not check nor parse chunk subdata, so it
// should work with all future PNG specifications.
//
// Also, a possible caveat: PNG encourages the type of chunks to be
// mapped to strings of the form "[a-zA-Z]{4}". However, it's not a requirement.
//
// Summary of PNG Format:
// A PNG file consists of an 8 byte ID followed by a series of chunks.
// Each Chunk is
// length (4 bytes), chunk_type (4 bytes), data (length bytes), crc (4 bytes)
// CRC Does NOT include the length bytes.
//--------------------------------------
BigEndian(); // PNG files are in Network Byte order
const uint64 PNGMAGIC = 0x89504E470D0A1A0AL;
// Chunk Type
typedef union {
uint32 ctype <format=hex>; // Chunk Type
char cname[4]; // character representation
} CTYPE <read=readCTYPE>;
string readCTYPE(local CTYPE &t) {
return t.cname;
}
// -- Specific Chunks
// IHDR - Image Header
typedef struct {
uint32 width;
uint32 height;
ubyte bits;
ubyte color_type;
ubyte compression;
ubyte filter;
ubyte interlace;
} IHDR <read=readIHDR>;
string readIHDR(local IHDR &ihdr) {
local string s;
SPrintf(s, "%i x %i (x%i)", ihdr.width, ihdr.height, ihdr.bits);
return s;
}
// tEXt - Text Data
typedef struct {
string label; // to the first NULL (including)
char data[length - Strlen(label) - 1]; // rest of the data
} tEXt <read=readtEXt>;
string readtEXt(local tEXt &text) {
local string s;
SPrintf(s, "%s = %s", text.label, text.data);
return s;
}
// -- End: Specific Chunks
local uint32 CHUNK_CNT = 0;
// Generic Chunks
typedef struct {
uint32 length; // Number of data bytes (not including length,type, or crc)
local quad pos_start = FTell();
CTYPE type; // Type of chunk
if (type.cname == "IHDR") {
IHDR ihdr;
} else if (type.cname == "tEXt") {
tEXt text;
} else {
ubyte data[length]; // Data (or not present)
}
local quad data_size = FTell() - pos_start;
uint32 crc <format=hex>; // CRC type and data (not including length or crc)
local uint32 crc_calc = Checksum(CHECKSUM_CRC32, pos_start, data_size);
if (crc != crc_calc) {
local string msg;
SPrintf(msg, "*WARNING CRC Mismatch @ chunk[%d] (%08x != %08x)\n", CHUNK_CNT, crc, crc_calc);
Warning(msg);
Printf(msg);
}
CHUNK_CNT++;
} CHUNK <read=readCHUNK>;
// Chunks can be in any order: HOWEVER, IHDR must be first, IEND must be last
// Bit 5s in chunk type bytes are used to flag some things:
// Ancillary bit: bit 5 of 1st byte: 0=Critical, 1=Ancillary
// Private bit: bit 5 of 2nd byte: 0=Public, 1=Private
// Reserved bit: bit 5 of 3rd byte: MUST be 0
// Safe to Copy bit: bit 5 of 4th byte: 0=Unsafe to Copy, 1=Safe to Copy
string readCHUNK(local CHUNK &c) {
local string s;
s=readCTYPE(c.type)+" (";
s += (c.type.cname[0] & 0x20) ? "Ancillary, " : "Critical, ";
s += (c.type.cname[1] & 0x20) ? "Private, " : "Public, ";
s += (c.type.cname[2] & 0x20) ? "ERROR_RESERVED, " : "";
s += (c.type.cname[3] & 0x20) ? "Safe to Copy)" : "Unsafe to Copy)";
return s;
}
// ---------------------------------------------------------------------------
// MAIN -- Here's where we really allocate the data
// ---------------------------------------------------------------------------
uint64 pngid <format=hex>;
if (pngid != PNGMAGIC) {
Warning("Invalid PNG File: Bad Magic Number");
return -1;
}
while(!FEof()) {
CHUNK chunk;
}
if (CHUNK_CNT > 1) {
if ((chunk[0].type.cname != "IHDR") || (chunk[CHUNK_CNT-1].type.cname != "IEND")) {
local string msg;
SPrintf(msg, "*WARNING: Chunk IHDR must be first and chunk IEND must be last!\n");
Warning(msg);
Printf(msg);
}
}