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