1 //=============================================================================
2 //
3 // Adventure Game Studio (AGS)
4 //
5 // Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
6 // The full list of copyright holders can be found in the Copyright.txt
7 // file, which is part of this source code distribution.
8 //
9 // The AGS source code is provided under the Artistic License 2.0.
10 // A copy of this license can be found in the file License.txt and at
11 // http://www.opensource.org/licenses/artistic-license-2.0.php
12 //
13 //=============================================================================
14
15 #include <algorithm>
16 #include <memory>
17 #include "font/wfnfont.h"
18 #include "debug/assert.h"
19 #include "debug/out.h"
20 #include "util/memory.h"
21
22 using namespace AGS::Common;
23
24 static const char *WFN_FILE_SIGNATURE = "WGT Font File ";
25 static const size_t WFN_FILE_SIG_LENGTH = 15;
26 static const size_t MinCharDataSize = sizeof(uint16_t) * 2;
27
WFNChar()28 WFNChar::WFNChar()
29 : Width(0)
30 , Height(0)
31 , Data(NULL)
32 {
33 }
34
RestrictToBytes(size_t bytes)35 void WFNChar::RestrictToBytes(size_t bytes)
36 {
37 if (bytes < GetRequiredPixelSize())
38 Height = bytes / GetRowByteCount();
39 }
40
41 const WFNChar WFNFont::_emptyChar;
42
Clear()43 void WFNFont::Clear()
44 {
45 _refs.clear();
46 _items.clear();
47 _pixelData.clear();
48 }
49
ReadFromFile(Stream * in,const size_t data_size)50 WFNError WFNFont::ReadFromFile(Stream *in, const size_t data_size)
51 {
52 Clear();
53
54 const size_t used_data_size = data_size > 0 ? data_size : in->GetLength();
55
56 // Read font header
57 char sig[WFN_FILE_SIG_LENGTH];
58 in->Read(sig, WFN_FILE_SIG_LENGTH);
59 if (strncmp(sig, WFN_FILE_SIGNATURE, WFN_FILE_SIG_LENGTH) != 0)
60 {
61 Debug::Printf(kDbgMsg_Error, "\tWFN: bad format signature");
62 return kWFNErr_BadSignature; // bad format
63 }
64
65 const size_t table_addr = (uint16_t)in->ReadInt16(); // offset table relative address
66 if (table_addr < WFN_FILE_SIG_LENGTH + sizeof(uint16_t) || table_addr >= used_data_size)
67 {
68 Debug::Printf(kDbgMsg_Error, "\tWFN: bad table address: %d (%d - %d)", table_addr, WFN_FILE_SIG_LENGTH + sizeof(uint16_t), used_data_size);
69 return kWFNErr_BadTableAddress; // bad table address
70 }
71
72 const size_t offset_table_size = used_data_size - table_addr;
73 const size_t raw_data_offset = WFN_FILE_SIG_LENGTH + sizeof(uint16_t);
74 const size_t total_char_data = table_addr - raw_data_offset;
75 const size_t char_count = offset_table_size / sizeof(uint16_t);
76
77 // We process character data in three steps:
78 // 1. For every character store offset of character item, excluding
79 // duplicates.
80 // 2. Allocate memory for character items and pixel array and copy
81 // appropriate data; test for possible format corruption.
82 // 3. Create array of references from characters to items; same item may be
83 // referenced by many characters.
84 WFNError err = kWFNErr_NoError;
85
86 // Read character data array
87 uint8_t *raw_data = new uint8_t[total_char_data];
88 in->Read(raw_data, total_char_data);
89
90 // Read offset table
91 uint16_t *offset_table = new uint16_t[char_count];
92 in->ReadArrayOfInt16((int16_t*)offset_table, char_count);
93
94 // Read all referenced offsets in an unsorted vector
95 std::vector<uint16_t> offs;
96 offs.reserve(char_count); // reserve max possible offsets
97 for (size_t i = 0; i < char_count; ++i)
98 {
99 const uint16_t off = offset_table[i];
100 if (off < raw_data_offset || off + MinCharDataSize > table_addr)
101 {
102 Debug::Printf("\tWFN: character %d -- bad item offset: %d (%d - %d, +%d)",
103 i, off, raw_data_offset, table_addr, MinCharDataSize);
104 err = kWFNErr_HasBadCharacters; // warn about potentially corrupt format
105 continue; // bad character offset
106 }
107 offs.push_back(off);
108 }
109 // sort offsets vector and remove any duplicates
110 std::sort(offs.begin(), offs.end());
111 std::vector<uint16_t>(offs.begin(), std::unique(offs.begin(), offs.end())).swap(offs);
112
113 // Now that we know number of valid character items, parse and store character data
114 WFNChar init_ch;
115 _items.resize(offs.size());
116 size_t total_pixel_size = 0;
117 for (size_t i = 0; i < _items.size(); ++i)
118 {
119 const uint8_t *p_data = raw_data + offs[i] - raw_data_offset;
120 init_ch.Width = Memory::ReadInt16LE(p_data);
121 init_ch.Height = Memory::ReadInt16LE(p_data + sizeof(uint16_t));
122 total_pixel_size += init_ch.GetRequiredPixelSize();
123 _items[i] = init_ch;
124 }
125
126 // Now that we know actual size of pixels in use, create pixel data array;
127 // since the items are sorted, the pixel data will be stored sequentially as well.
128 // At this point offs and _items have related elements in the same order.
129 _pixelData.resize(total_pixel_size);
130 std::vector<uint8_t>::iterator pixel_it = _pixelData.begin(); // write ptr
131 for (size_t i = 0; i < _items.size(); ++i)
132 {
133 const size_t pixel_data_size = _items[i].GetRequiredPixelSize();
134 if (pixel_data_size == 0)
135 {
136 Debug::Printf("\tWFN: item at off %d -- null size", offs[i]);
137 err = kWFNErr_HasBadCharacters;
138 continue; // just an empty character
139 }
140 const uint16_t raw_off = offs[i] - raw_data_offset + MinCharDataSize; // offset in raw array
141 size_t src_size = pixel_data_size;
142 if (i + 1 != _items.size() && raw_off + src_size > offs[i + 1] - raw_data_offset)
143 { // character pixel data overlaps next character
144 Debug::Printf("\tWFN: item at off %d -- pixel data overlaps next known item (at %d, +%d)",
145 offs[i], offs[i + 1], MinCharDataSize + src_size);
146 err = kWFNErr_HasBadCharacters; // warn about potentially corrupt format
147 src_size = offs[i + 1] - offs[i] - MinCharDataSize;
148 }
149
150 if (raw_off + src_size > total_char_data)
151 { // character pixel data overflow buffer
152 Debug::Printf("\tWFN: item at off %d -- pixel data exceeds available data (at %d, +%d)",
153 offs[i], table_addr, MinCharDataSize + src_size);
154 err = kWFNErr_HasBadCharacters; // warn about potentially corrupt format
155 src_size = total_char_data - raw_off;
156 }
157 _items[i].RestrictToBytes(src_size);
158
159 assert(pixel_it + pixel_data_size <= _pixelData.end()); // should not normally fail
160 std::copy(raw_data + raw_off, raw_data + raw_off + src_size, pixel_it);
161 _items[i].Data = &(*pixel_it);
162 pixel_it += pixel_data_size;
163 }
164
165 // Create final reference array
166 _refs.resize(char_count);
167 for (size_t i = 0; i < char_count; ++i)
168 {
169 const uint16_t off = offset_table[i];
170 // if bad character offset - reference empty character
171 if (off < raw_data_offset || off + MinCharDataSize > table_addr)
172 {
173 _refs[i] = &_emptyChar;
174 }
175 else
176 {
177 // in usual case the offset table references items in strict order
178 if (i < _items.size() && offs[i] == off)
179 _refs[i] = &_items[i];
180 else
181 {
182 // we know beforehand that such item must exist
183 std::vector<uint16_t>::const_iterator at = std::lower_bound(offs.begin(), offs.end(), off);
184 assert(at != offs.end() && *at == off && // should not normally fail
185 at - offs.begin() >= 0 && (size_t)(at - offs.begin()) < _items.size());
186 _refs[i] = &_items[at - offs.begin()]; // set up reference to item
187 }
188 }
189 }
190
191 delete [] raw_data;
192 delete [] offset_table;
193 return err;
194 }
195