void NutRenderer::loadFont(const char *filename) { ScummFile file; _vm->openFile(file, filename); if (!file.isOpen()) { error("NutRenderer::loadFont() Can't open font file: %s", filename); } uint32 tag = file.readUint32BE(); if (tag != MKID_BE('ANIM')) { error("NutRenderer::loadFont() there is no ANIM chunk in font header"); } uint32 length = file.readUint32BE(); byte *dataSrc = new byte[length]; file.read(dataSrc, length); file.close(); if (READ_BE_UINT32(dataSrc) != MKID_BE('AHDR')) { error("NutRenderer::loadFont() there is no AHDR chunk in font header"); } // We pre-decode the font, which may seem wasteful at first. Actually, // the memory needed for just the decoded glyphs is smaller than the // whole of the undecoded font file. _numChars = READ_LE_UINT16(dataSrc + 10); assert(_numChars <= ARRAYSIZE(_chars)); uint32 offset = 0; uint32 decodedLength = 0; int l; _paletteMap = new byte[256]; for (l = 0; l < 256; l++) { _paletteMap[l] = 0; } for (l = 0; l < _numChars; l++) { offset += READ_BE_UINT32(dataSrc + offset + 4) + 16; int width = READ_LE_UINT16(dataSrc + offset + 14); int height = READ_LE_UINT16(dataSrc + offset + 16); int size = width * height; decodedLength += size; if (size > _maxCharSize) _maxCharSize = size; } debug(1, "NutRenderer::loadFont('%s') - decodedLength = %d", filename, decodedLength); _decodedData = new byte[decodedLength]; byte *decodedPtr = _decodedData; offset = 0; for (l = 0; l < _numChars; l++) { offset += READ_BE_UINT32(dataSrc + offset + 4) + 8; if (READ_BE_UINT32(dataSrc + offset) != MKID_BE('FRME')) { error("NutRenderer::loadFont(%s) there is no FRME chunk %d (offset %x)", filename, l, offset); break; } offset += 8; if (READ_BE_UINT32(dataSrc + offset) != MKID_BE('FOBJ')) { error("NutRenderer::loadFont(%s) there is no FOBJ chunk in FRME chunk %d (offset %x)", filename, l, offset); break; } int codec = READ_LE_UINT16(dataSrc + offset + 8); // _chars[l].xoffs = READ_LE_UINT16(dataSrc + offset + 10); // _chars[l].yoffs = READ_LE_UINT16(dataSrc + offset + 12); _chars[l].width = READ_LE_UINT16(dataSrc + offset + 14); _chars[l].height = READ_LE_UINT16(dataSrc + offset + 16); _chars[l].src = decodedPtr; decodedPtr += (_chars[l].width * _chars[l].height); // If characters have transparency, then bytes just get skipped and // so there may appear some garbage. That's why we have to fill it // with a default color first. if (codec == 44) { memset(_chars[l].src, kSmush44TransparentColor, _chars[l].width * _chars[l].height); _paletteMap[kSmush44TransparentColor] = 1; _chars[l].transparency = kSmush44TransparentColor; } else { memset(_chars[l].src, kDefaultTransparentColor, _chars[l].width * _chars[l].height); _paletteMap[kDefaultTransparentColor] = 1; _chars[l].transparency = kDefaultTransparentColor; } const uint8 *fobjptr = dataSrc + offset + 22; switch (codec) { case 1: codec1(_chars[l].src, fobjptr, _chars[l].width, _chars[l].height, _chars[l].width); break; case 21: case 44: codec21(_chars[l].src, fobjptr, _chars[l].width, _chars[l].height, _chars[l].width); break; default: error("NutRenderer::loadFont: unknown codec: %d", codec); } } // We have decoded the font. Now let's see if we can re-compress it to // a more compact format. Start by counting the number of colors. int numColors = 0; for (l = 0; l < 256; l++) { if (_paletteMap[l]) { if (numColors < ARRAYSIZE(_palette)) { _paletteMap[l] = numColors; _palette[numColors] = l; } numColors++; } } // Now _palette contains all the used colors, and _paletteMap maps the // real color to the palette index. if (numColors <= 2) _bpp = 1; else if (numColors <= 4) _bpp = 2; else if (numColors <= 16) _bpp = 4; else _bpp = 8; if (_bpp < 8) { int compressedLength = 0; for (l = 0; l < 256; l++) { compressedLength += (((_bpp * _chars[l].width + 7) / 8) * _chars[l].height); } debug(1, "NutRenderer::loadFont('%s') - compressedLength = %d (%d bpp)", filename, compressedLength, _bpp); byte *compressedData = new byte[compressedLength]; memset(compressedData, 0, compressedLength); offset = 0; for (l = 0; l < 256; l++) { byte *src = _chars[l].src; byte *dst = compressedData + offset; int srcPitch = _chars[l].width; int dstPitch = (_bpp * _chars[l].width + 7) / 8; for (int h = 0; h < _chars[l].height; h++) { byte bit = 0x80; byte *nextDst = dst + dstPitch; for (int w = 0; w < srcPitch; w++) { byte color = _paletteMap[src[w]]; for (int i = 0; i < _bpp; i++) { if (color & (1 << i)) *dst |= bit; bit >>= 1; } if (!bit) { bit = 0x80; dst++; } } src += srcPitch; dst = nextDst; } _chars[l].src = compressedData + offset; offset += (dstPitch * _chars[l].height); } delete[] _decodedData; _decodedData = compressedData; _charBuffer = new byte[_maxCharSize]; }
int BundleDirCache::matchFile(const char *filename) { int32 tag, offset; bool found = false; int freeSlot = -1; int fileId; for (fileId = 0; fileId < ARRAYSIZE(_budleDirCache); fileId++) { if ((_budleDirCache[fileId].bundleTable == NULL) && (freeSlot == -1)) { freeSlot = fileId; } if (scumm_stricmp(filename, _budleDirCache[fileId].fileName) == 0) { found = true; break; } } if (!found) { ScummFile file; if (g_scumm->openFile(file, filename) == false) { error("BundleDirCache::matchFile() Can't open bundle file: %s", filename); return false; } if (freeSlot == -1) error("BundleDirCache::matchFileFile() Can't find free slot for file bundle dir cache"); tag = file.readUint32BE(); if (tag == 'LB23') _budleDirCache[freeSlot].compressedBun = true; offset = file.readUint32BE(); strcpy(_budleDirCache[freeSlot].fileName, filename); _budleDirCache[freeSlot].numFiles = file.readUint32BE(); _budleDirCache[freeSlot].bundleTable = (AudioTable *)malloc(_budleDirCache[freeSlot].numFiles * sizeof(AudioTable)); assert(_budleDirCache[freeSlot].bundleTable); file.seek(offset, SEEK_SET); _budleDirCache[freeSlot].indexTable = (IndexNode *)calloc(_budleDirCache[freeSlot].numFiles, sizeof(IndexNode)); assert(_budleDirCache[freeSlot].indexTable); for (int32 i = 0; i < _budleDirCache[freeSlot].numFiles; i++) { char name[24], c; int32 z = 0; int32 z2; if (tag == 'LB23') { file.read(_budleDirCache[freeSlot].bundleTable[i].filename, 24); } else { for (z2 = 0; z2 < 8; z2++) if ((c = file.readByte()) != 0) name[z++] = c; name[z++] = '.'; for (z2 = 0; z2 < 4; z2++) if ((c = file.readByte()) != 0) name[z++] = c; name[z] = '\0'; strcpy(_budleDirCache[freeSlot].bundleTable[i].filename, name); } _budleDirCache[freeSlot].bundleTable[i].offset = file.readUint32BE(); _budleDirCache[freeSlot].bundleTable[i].size = file.readUint32BE(); strcpy(_budleDirCache[freeSlot].indexTable[i].filename, _budleDirCache[freeSlot].bundleTable[i].filename); _budleDirCache[freeSlot].indexTable[i].index = i; } qsort(_budleDirCache[freeSlot].indexTable, _budleDirCache[freeSlot].numFiles, sizeof(IndexNode), (int (*)(const void*, const void*))scumm_stricmp); return freeSlot; } else { return fileId; } }