| 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 | | |