83 | | static int32 codec1(byte *dst, byte *src, int height) { |
84 | | byte val, code; |
85 | | int32 length, decoded_length = 0; |
86 | | int h = height, size_line; |
87 | | |
88 | | for (h = 0; h < height; h++) { |
89 | | size_line = READ_LE_UINT16(src); |
90 | | src += 2; |
91 | | while (size_line > 0) { |
92 | | code = *src++; |
93 | | size_line--; |
94 | | length = (code >> 1) + 1; |
95 | | if (code & 1) { |
96 | | val = *src++; |
97 | | size_line--; |
98 | | if (val) |
99 | | memset(dst, val, length); |
100 | | dst += length; |
101 | | decoded_length += length; |
102 | | } else { |
103 | | size_line -= length; |
104 | | while (length--) { |
105 | | val = *src++; |
106 | | if (val) |
107 | | *dst = val; |
108 | | dst++; |
109 | | decoded_length++; |
110 | | } |
| 52 | static void smush_decode_codec21(byte *dst, const byte *src, int width, int height, int pitch) { |
| 53 | while (height--) { |
| 54 | uint8 *dstPtrNext = dst + pitch; |
| 55 | assert(src + 2 <= fobj_end); |
| 56 | const uint8 *srcPtrNext = src + 2 + READ_LE_UINT16(src); src += 2; |
| 57 | int len = width; |
| 58 | do { |
| 59 | assert(src + 2 <= fobj_end); |
| 60 | int offs = READ_LE_UINT16(src); src += 2; |
| 61 | dst += offs; |
| 62 | len -= offs; |
| 63 | if (len <= 0) { |
| 64 | break; |
112 | | } |
| 66 | assert(src + 2 <= fobj_end); |
| 67 | int w = READ_LE_UINT16(src) + 1; src += 2; |
| 68 | len -= w; |
| 69 | if (len < 0) { |
| 70 | w += len; |
| 71 | } |
| 72 | // the original codec44 handles this part slightly differently (this is the only difference with codec21) : |
| 73 | // src bytes equal to 255 are replaced by 0 in dst |
| 74 | // src bytes equal to 1 are replaced by a color passed as an argument in the original function |
| 75 | // other src bytes values are copied as-is |
| 76 | assert(dst + w <= dst_end); |
| 77 | assert(src + w <= fobj_end); |
| 78 | memcpy(dst, src, w); dst += w; src += w; |
| 79 | } while (len > 0); |
| 80 | dst = dstPtrNext; |
| 81 | src = srcPtrNext; |
152 | | if (READ_BE_UINT32(dataSrc + offset) == 'FRME') { |
153 | | offset += 8; |
154 | | |
155 | | if (READ_BE_UINT32(dataSrc + offset) == 'FOBJ') { |
156 | | int codec = READ_LE_UINT16(dataSrc + offset + 8); |
157 | | _chars[l].xoffs = READ_LE_UINT16(dataSrc + offset + 10); |
158 | | _chars[l].yoffs = READ_LE_UINT16(dataSrc + offset + 12); |
159 | | _chars[l].width = READ_LE_UINT16(dataSrc + offset + 14); |
160 | | _chars[l].height = READ_LE_UINT16(dataSrc + offset + 16); |
161 | | _chars[l].src = new byte[(_chars[l].width + 2) * _chars[l].height + 1000]; |
162 | | |
163 | | // If characters have transparency, then bytes just get skipped and |
164 | | // so there may appear some garbage. That's why we have to fill it |
165 | | // with zeroes first. |
166 | | memset(_chars[l].src, 0, (_chars[l].width + 2) * _chars[l].height + 1000); |
167 | | if ((codec == 44) || (codec == 21)) |
168 | | decoded_length = decodeCodec44(_chars[l].src, dataSrc + offset + 22, READ_BE_UINT32(dataSrc + offset + 4) - 14); |
169 | | else if (codec == 1) { |
170 | | decoded_length = codec1(_chars[l].src, dataSrc + offset + 22, _chars[l].height); |
171 | | } else |
172 | | error("NutRenderer::loadFont: unknown codec: %d", codec); |
173 | | |
174 | | // FIXME: This is used to work around wrong font file format in Russian |
175 | | // version of FT. Font files there contain wrong information about |
176 | | // glyphs width. See patch #823031. |
177 | | if (_vm->_language == Common::RU_RUS) { |
178 | | // try to rely on length of returned data |
179 | | if (l > 127) |
180 | | _chars[l].width = decoded_length / _chars[l].height; |
181 | | // but even this not always works |
182 | | if (l == 134 && !strcmp(filename, "titlfnt.nut")) |
183 | | _chars[l].width--; |
184 | | } |
185 | | } else { |
186 | | warning("NutRenderer::loadFont(%s) there is no FOBJ chunk in FRME chunk %d (offset %x)", filename, l, offset); |
187 | | break; |
188 | | } |
189 | | } else { |
| 118 | if (READ_BE_UINT32(dataSrc + offset) != 'FRME') { |
| 122 | offset += 8; |
| 123 | if (READ_BE_UINT32(dataSrc + offset) != 'FOBJ') { |
| 124 | warning("NutRenderer::loadFont(%s) there is no FOBJ chunk in FRME chunk %d (offset %x)", filename, l, offset); |
| 125 | break; |
| 126 | } |
| 127 | int codec = READ_LE_UINT16(dataSrc + offset + 8); |
| 128 | _chars[l].xoffs = READ_LE_UINT16(dataSrc + offset + 10); |
| 129 | _chars[l].yoffs = READ_LE_UINT16(dataSrc + offset + 12); |
| 130 | _chars[l].width = READ_LE_UINT16(dataSrc + offset + 14); |
| 131 | _chars[l].height = READ_LE_UINT16(dataSrc + offset + 16); |
| 132 | const int srcSize = _chars[l].width * _chars[l].height; |
| 133 | _chars[l].src = new byte[srcSize]; |
| 134 | // If characters have transparency, then bytes just get skipped and |
| 135 | // so there may appear some garbage. That's why we have to fill it |
| 136 | // with zeroes first. |
| 137 | memset(_chars[l].src, 0, srcSize); |
| 138 | |
| 139 | const uint8 *fobjptr = dataSrc + offset + 22; |
| 140 | fobj_end = dataSrc + offset + 22 + READ_BE_UINT32(dataSrc + offset + 4) - 14; |
| 141 | dst_end = _chars[l].src + srcSize; |
| 142 | switch (codec) { |
| 143 | case 1: |
| 144 | smush_decode_codec1(_chars[l].src, fobjptr, 0, 0, _chars[l].width, _chars[l].height, _chars[l].width); |
| 145 | break; |
| 146 | case 21: |
| 147 | case 44: |
| 148 | smush_decode_codec21(_chars[l].src, fobjptr, _chars[l].width, _chars[l].height, _chars[l].width); |
| 149 | break; |
| 150 | default: |
| 151 | error("NutRenderer::loadFont: unknown codec: %d", codec); |
| 152 | } |
230 | | int NutRenderer::getCharOffsX(byte c) const { |
231 | | if (!_loaded) { |
232 | | warning("NutRenderer::getCharOffsX() Font is not loaded"); |
233 | | return 0; |
234 | | } |
235 | | |
236 | | if (c >= 0x80 && _vm->_useCJKMode) { |
237 | | return 0; |
238 | | } |
239 | | |
240 | | if (c >= _numChars) |
241 | | error("invalid character in NutRenderer::getCharOffsX : %d (%d)", c, _numChars); |
242 | | |
243 | | return _chars[c].xoffs; |
244 | | } |
245 | | |
246 | | int NutRenderer::getCharOffsY(byte c) const { |
247 | | if (!_loaded) { |
248 | | warning("NutRenderer::getCharOffsY() Font is not loaded"); |
249 | | return 0; |
250 | | } |
251 | | |
252 | | if (c >= 0x80 && _vm->_useCJKMode) { |
253 | | return 0; |
254 | | } |
255 | | |
256 | | if (c >= _numChars) |
257 | | error("invalid character in NutRenderer::getCharOffsY : %d (%d)", c, _numChars); |
258 | | |
259 | | return _chars[c].yoffs; |
260 | | } |
261 | | |