File: wfn_font.cpp

package info (click to toggle)
scummvm 2.9.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 450,580 kB
  • sloc: cpp: 4,299,825; asm: 28,322; python: 12,901; sh: 11,302; java: 9,289; xml: 7,895; perl: 2,639; ansic: 2,465; yacc: 1,670; javascript: 1,020; makefile: 933; lex: 578; awk: 275; objc: 82; sed: 11; php: 1
file content (196 lines) | stat: -rw-r--r-- 7,872 bytes parent folder | download | duplicates (2)
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "common/std/algorithm.h"
#include "ags/shared/font/wfn_font.h"
#include "ags/shared/debugging/out.h"
#include "ags/shared/util/memory.h"
#include "ags/shared/util/stream.h"
#include "ags/globals.h"

namespace AGS3 {

using namespace AGS::Shared;

static const char *WFN_FILE_SIGNATURE = "WGT Font File  ";
static const size_t  WFN_FILE_SIG_LENGTH = 15;
static const size_t  MinCharDataSize = sizeof(uint16_t) * 2;

WFNChar::WFNChar()
	: Width(0)
	, Height(0)
	, Data(nullptr) {
}

void WFNChar::RestrictToBytes(size_t bytes) {
	if (bytes < GetRequiredPixelSize())
		Height = static_cast<uint16_t>(bytes / GetRowByteCount());
}

const WFNChar &WFNFont::GetChar(uint16_t code) const {
	return code < _refs.size() ? *_refs[code] : _G(emptyChar);
}

void WFNFont::Clear() {
	_refs.clear();
	_items.clear();
	_pixelData.clear();
}

WFNError WFNFont::ReadFromFile(Stream *in, const soff_t data_size) {
	Clear();

	const soff_t used_data_size = data_size > 0 ? data_size : in->GetLength();

	// Read font header
	char sig[WFN_FILE_SIG_LENGTH];
	in->Read(sig, WFN_FILE_SIG_LENGTH);
	if (strncmp(sig, WFN_FILE_SIGNATURE, WFN_FILE_SIG_LENGTH) != 0) {
		Debug::Printf(kDbgMsg_Error, "\tWFN: bad format signature");
		return kWFNErr_BadSignature; // bad format
	}

	const soff_t table_addr = static_cast<uint16_t>(in->ReadInt16()); // offset table relative address
	if (table_addr < (soff_t)(WFN_FILE_SIG_LENGTH + sizeof(uint16_t)) || table_addr >= used_data_size) {
		Debug::Printf(kDbgMsg_Error, "\tWFN: bad table address: %llu (%llu - %llu)", static_cast<int64>(table_addr),
			static_cast<int64>(WFN_FILE_SIG_LENGTH + sizeof(uint16_t)), static_cast<int64>(used_data_size));
		return kWFNErr_BadTableAddress; // bad table address
	}

	const soff_t offset_table_size = used_data_size - table_addr;
	const soff_t raw_data_offset = WFN_FILE_SIG_LENGTH + sizeof(uint16_t);
	const size_t total_char_data = static_cast<size_t>(table_addr - raw_data_offset);
	const size_t char_count = static_cast<size_t>(offset_table_size / sizeof(uint16_t));

	// We process character data in three steps:
	// 1. For every character store offset of character item, excluding
	//    duplicates.
	// 2. Allocate memory for character items and pixel array and copy
	//    appropriate data; test for possible format corruption.
	// 3. Create array of references from characters to items; same item may be
	//    referenced by many characters.
	WFNError err = kWFNErr_NoError;

	if (total_char_data == 0u || char_count == 0u)
		return kWFNErr_NoError; // no items

	// Read character data array
	std::vector<uint8_t> raw_data; raw_data.resize(total_char_data);
	in->Read(&raw_data.front(), total_char_data);

	// Read offset table
	std::vector<uint16_t> offset_table;	offset_table.resize(char_count);
	in->ReadArrayOfInt16(reinterpret_cast<int16_t *>(&offset_table.front()), char_count);

	// Read all referenced offsets in an unsorted vector
	std::vector<uint16_t> offs;
	offs.reserve(char_count); // reserve max possible offsets
	for (size_t i = 0; i < char_count; ++i) {
		const uint16_t off = offset_table[i];
		if (off < raw_data_offset || (soff_t)(off + MinCharDataSize) > table_addr) {
			Debug::Printf("\tWFN: character %d -- bad item offset: %d (%d - %d, +%d)",
			              i, off, raw_data_offset, table_addr, MinCharDataSize);
			err = kWFNErr_HasBadCharacters; // warn about potentially corrupt format
			continue; // bad character offset
		}
		offs.push_back(off);
	}
	// sort offsets vector and remove any duplicates
	std::sort(offs.begin(), offs.end());
#if AGS_PLATFORM_SCUMMVM
	// TODO: See if this works correctly
	std::unique(offs.begin(), offs.end());
#else
	std::vector<uint16_t>(offs.begin(), std::unique(offs.begin(), offs.end())).swap(offs);
#endif

	// Now that we know number of valid character items, parse and store character data
	WFNChar init_ch;
	_items.resize(offs.size());
	size_t total_pixel_size = 0;
	for (size_t i = 0; i < _items.size(); ++i) {
		const uint8_t *p_data = &raw_data[offs[i] - raw_data_offset];
		init_ch.Width = Memory::ReadInt16LE(p_data);
		init_ch.Height = Memory::ReadInt16LE(p_data + sizeof(uint16_t));
		total_pixel_size += init_ch.GetRequiredPixelSize();
		_items[i] = init_ch;
	}

	// Now that we know actual size of pixels in use, create pixel data array;
	// since the items are sorted, the pixel data will be stored sequentially as well.
	// At this point offs and _items have related elements in the same order.
	_pixelData.resize(total_pixel_size);
	std::vector<uint8_t>::iterator pixel_it = _pixelData.begin(); // write ptr
	for (size_t i = 0; i < _items.size(); ++i) {
		const size_t pixel_data_size = _items[i].GetRequiredPixelSize();
		if (pixel_data_size == 0) {
			Debug::Printf("\tWFN: item at off %d -- null size", offs[i]);
			err = kWFNErr_HasBadCharacters;
			continue; // just an empty character
		}
		const uint16_t raw_off = offs[i] - raw_data_offset + MinCharDataSize; // offset in raw array
		size_t src_size = pixel_data_size;
		if (i + 1 != _items.size() && (soff_t)(raw_off + src_size) > offs[i + 1] - raw_data_offset) {   // character pixel data overlaps next character
			Debug::Printf("\tWFN: item at off %d -- pixel data overlaps next known item (at %d, +%d)",
			              offs[i], offs[i + 1], MinCharDataSize + src_size);
			err = kWFNErr_HasBadCharacters; // warn about potentially corrupt format
			src_size = offs[i + 1] - offs[i] - MinCharDataSize;
		}

		if (raw_off + src_size > total_char_data) {   // character pixel data overflow buffer
			Debug::Printf("\tWFN: item at off %d -- pixel data exceeds available data (at %d, +%d)",
			              offs[i], table_addr, MinCharDataSize + src_size);
			err = kWFNErr_HasBadCharacters; // warn about potentially corrupt format
			src_size = total_char_data - raw_off;
		}
		_items[i].RestrictToBytes(src_size);

		assert(pixel_it + pixel_data_size <= _pixelData.end()); // should not normally fail
		Common::copy(raw_data.begin() + raw_off, raw_data.begin() + (raw_off + src_size), pixel_it);
		_items[i].Data = &(*pixel_it);
		pixel_it += pixel_data_size;
	}

	// Create final reference array
	_refs.resize(char_count);
	for (size_t i = 0; i < char_count; ++i) {
		const uint16_t off = offset_table[i];
		// if bad character offset - reference empty character
		if (off < raw_data_offset || (soff_t)(off + MinCharDataSize) > table_addr) {
			_refs[i] = &_G(emptyChar);
		} else {
			// in usual case the offset table references items in strict order
			if (i < _items.size() && offs[i] == off)
				_refs[i] = &_items[i];
			else {
				// we know beforehand that such item must exist
				std::vector<uint16_t>::const_iterator at = std::lower_bound(offs.begin(), offs.end(), off);
				assert(at != offs.end() && *at == off && // should not normally fail
				       at - offs.begin() >= 0 && static_cast<size_t>(at - offs.begin()) < _items.size());
				_refs[i] = &_items[at - offs.begin()]; // set up reference to item
			}
		}
	}

	return err;
}

} // namespace AGS3