/* ScummVM - Scumm Interpreter * Copyright (C) 2001-2006 The ScummVM project * * 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $URL$ * $Id$ * */ #include "common/stdafx.h" #include "common/file.h" #include "simon/simon.h" #include "simon/intern.h" namespace Simon { void SimonEngine::loadIconFile() { Common::File in; if (getPlatform() == Common::kPlatformAcorn) in.open("ICONDATA"); else if (getPlatform() == Common::kPlatformAmiga) in.open("icon.pkd"); else in.open("ICON.DAT"); uint size; if (in.isOpen() == false) error("Can't open icons file 'ICON.DAT'"); size = in.size(); _iconFilePtr = (byte *)malloc(size); if (_iconFilePtr == NULL) error("Out of icon memory"); in.read(_iconFilePtr, size); in.close(); } void SimonEngine::loadIconData() { loadZone(8); VgaPointersEntry *vpe = &_vgaBufferPointers[8]; // FIXME: For reasons unknown, the first 16 bytes are not part of the // actual image data. This could be an indication of some deeper // problem elsewhere. For now, adding 16 fixes the drawing of the // Oracle icon. byte *src = vpe->vgaFile2 + 16; _iconFilePtr = (byte *)malloc(43 * 336); if (_iconFilePtr == NULL) error("Out of icon memory"); memcpy(_iconFilePtr, src, 43 * 336); o_unfreezeBottom(); } // Thanks to Stuart Caie for providing the original // C conversion upon which this function is based. void decompressIconAmiga (byte *dst, byte *src, byte base, uint pitch) { byte icon_pln[288]; byte *i, *o, x, y; // Decode RLE planar icon data i = src; o = icon_pln; while (o < &icon_pln[288]) { x = *i++; if (x < 128) { do { *o++ = *i++; *o++ = *i++; *o++ = *i++; } while (x-- > 0); } else { x = 256 - x; do { *o++ = i[0]; *o++ = i[1]; *o++ = i[2]; } while (x-- > 0); i += 3; } } // Translate planar data to chunky (very slow method) for (y = 0; y < 24; y++) { for (x = 0; x < 24; x++) { byte pixel = (icon_pln[(( y) * 3) + (x >> 3)] & (1 << (7 - (x & 7))) ? 1 : 0) | (icon_pln[((24 + y) * 3) + (x >> 3)] & (1 << (7 - (x & 7))) ? 2 : 0) | (icon_pln[((48 + y) * 3) + (x >> 3)] & (1 << (7 - (x & 7))) ? 4 : 0) | (icon_pln[((72 + y) * 3) + (x >> 3)] & (1 << (7 - (x & 7))) ? 8 : 0); if (pixel) dst[x] = pixel | base; } dst += pitch; } } static void decompressIcon(byte *dst, byte *src, uint w, uint h_org, byte base, uint pitch) { int8 reps; byte color_1, color_2; byte *dst_org = dst; uint h = h_org; for (;;) { reps = *src++; if (reps < 0) { reps--; color_1 = *src >> 4; if (color_1 != 0) color_1 |= base; color_2 = *src++ & 0xF; if (color_2 != 0) color_2 |= base; do { if (color_1 != 0) *dst = color_1; dst += pitch; if (color_2 != 0) *dst = color_2; dst += pitch; // reached bottom? if (--h == 0) { // reached right edge? if (--w == 0) return; dst = ++dst_org; h = h_org; } } while (++reps != 0); } else { do { color_1 = *src >> 4; if (color_1 != 0) *dst = color_1 | base; dst += pitch; color_2 = *src++ & 0xF; if (color_2 != 0) *dst = color_2 | base; dst += pitch; // reached bottom? if (--h == 0) { // reached right edge? if (--w == 0) return; dst = ++dst_org; h = h_org; } } while (--reps >= 0); } } } void SimonEngine::draw_icon_c(WindowBlock *window, uint icon, uint x, uint y) { byte *dst; byte *src; _lockWord |= 0x8000; dst = getFrontBuf(); if (getGameType() == GType_SIMON1) { // Simon 1 dst += (x + window->x) * 8; dst += (y * 25 + window->y) * _dxSurfacePitch; if (getPlatform() == Common::kPlatformAmiga) { src = _iconFilePtr; src += READ_BE_UINT32(&((uint32 *)src)[icon]); decompressIconAmiga (dst, src, 224, _dxSurfacePitch); } else { src = _iconFilePtr; src += READ_LE_UINT16(&((uint16 *)src)[icon]); decompressIcon(dst, src, 24, 12, 224, _dxSurfacePitch); } } else { // Simon 2 dst += 110; dst += x; dst += (y + window->y) * _dxSurfacePitch; src = _iconFilePtr; src += READ_LE_UINT16(&((uint16 *)src)[icon * 2 + 0]); decompressIcon(dst, src, 20, 10, 224, _dxSurfacePitch); src = _iconFilePtr; src += READ_LE_UINT16(&((uint16 *)src)[icon * 2 + 1]); decompressIcon(dst, src, 20, 10, 208, _dxSurfacePitch); } _lockWord &= ~0x8000; } void SimonEngine::drawIconArray(uint num, Item *itemRef, int line, int classMask) { if (getGameType() == GType_FF) { drawIconArray_FF(num, itemRef, line, classMask); } else { drawIconArray_Simon(num, itemRef, line, classMask); } } void SimonEngine::drawIconArray_Simon(uint num, Item *itemRef, int line, int classMask) { Item *item_ptr_org = itemRef; WindowBlock *window; uint width_div_3, height_div_3; uint j, k, i, num_sibs_with_flag; bool item_again; uint x_pos, y_pos; window = _windowArray[num & 7]; if (getGameType() == GType_SIMON1) { width_div_3 = window->width / 3; height_div_3 = window->height / 3; } else { width_div_3 = 100; height_div_3 = 40; } i = 0; if (window == NULL) return; if (window->iconPtr) removeIconArray(num); window->iconPtr = (IconBlock *) malloc(sizeof(IconBlock)); window->iconPtr->itemRef = itemRef; window->iconPtr->upArrow = -1; window->iconPtr->downArrow = -1; window->iconPtr->line = line; window->iconPtr->classMask = classMask; itemRef = derefItem(itemRef->child); while (itemRef && line-- != 0) { num_sibs_with_flag = 0; while (itemRef && width_div_3 > num_sibs_with_flag) { if ((classMask == 0 || itemRef->classFlags & classMask) && has_item_childflag_0x10(itemRef)) if (getGameType() == GType_SIMON1) { num_sibs_with_flag++; } else { num_sibs_with_flag += 20; } itemRef = derefItem(itemRef->sibling); } } if (itemRef == NULL) { window->iconPtr->line = 0; itemRef = derefItem(item_ptr_org->child); } x_pos = 0; y_pos = 0; item_again = false; k = 0; j = 0; while (itemRef) { if ((classMask == 0 || itemRef->classFlags & classMask) && has_item_childflag_0x10(itemRef)) { if (item_again == false) { window->iconPtr->iconArray[k].item = itemRef; if (getGameType() == GType_SIMON1) { draw_icon_c(window, itemGetIconNumber(itemRef), x_pos * 3, y_pos); window->iconPtr->iconArray[k].boxCode = setupIconHitArea(window, 0, x_pos * 3, y_pos, itemRef); } else { draw_icon_c(window, itemGetIconNumber(itemRef), x_pos, y_pos); window->iconPtr->iconArray[k].boxCode = setupIconHitArea(window, 0, x_pos, y_pos, itemRef); } k++; } else { window->iconPtr->iconArray[k].item = NULL; j = 1; } x_pos += (getGameType() == GType_SIMON1) ? 1 : 20; if (x_pos >= width_div_3) { x_pos = 0; y_pos += (getGameType() == GType_SIMON1) ? 1 : 20; if (y_pos >= height_div_3) item_again = true; } } itemRef = derefItem(itemRef->sibling); } window->iconPtr->iconArray[k].item = NULL; if (j != 0 || window->iconPtr->line != 0) { addArrows(window, num); } } void SimonEngine::drawIconArray_FF(uint num, Item *itemRef, int line, int classMask) { Item *item_ptr_org = itemRef; WindowBlock *window; uint16 flagnumber = 201; uint16 iconperline = 458; uint16 iconsdown = 384; uint16 idone = 0; uint16 icount = 0; uint16 xp = 188, yp = 306; int k; _iOverflow = 0; line = _variableArray[30]; if (line == 0) _variableArray[31] = 0; window = _windowArray[num & 7]; if (window == NULL) return; for (k = flagnumber; k <= flagnumber + 18; k++) _variableArray[k] = 0; if (window->iconPtr) removeIconArray(num); window->iconPtr=(IconBlock *)malloc(sizeof(IconBlock)); window->iconPtr->itemRef = itemRef; window->iconPtr->upArrow = -1; window->iconPtr->downArrow = -1; window->iconPtr->line = line; window->iconPtr->classMask = classMask; itemRef = derefItem(itemRef->child); k = flagnumber; while (itemRef && (line > 65)) { uint16 ct = xp; while (itemRef && ct < iconperline) { if ((classMask == 0) || ((itemRef->classFlags & classMask) != 0)) { if (has_item_childflag_0x10(itemRef)) { ct += 45; k++; } } itemRef = derefItem(itemRef->sibling); } line -= 52; if (k == (flagnumber + 18)) k = flagnumber; } yp -= line; // Adjust starting y if (itemRef == NULL) { window->iconPtr->line = 0; itemRef = derefItem(item_ptr_org->child); } while (itemRef) { if ((classMask != 0) && ((itemRef->classFlags & classMask) == 0)) goto l1; if (has_item_childflag_0x10(itemRef) == 0) goto l1; if (!idone) { /* * Create thee icon and graphics rendering */ window->iconPtr->iconArray[icount].item = itemRef; _variableArray[k] = itemGetIconNumber(itemRef); window->iconPtr->iconArray[icount++].boxCode = setupIconHitArea(window, k++, xp, yp, itemRef); } else { /* * Just remember the overflow has occured */ window->iconPtr->iconArray[icount].item = NULL; /* END MARKINGS */ _iOverflow = 1; } xp += 45; if (xp >= iconperline) { /* End of line ? */ if (k == (flagnumber + 18)) k = flagnumber; xp = 188; yp += 52; /* Move down */ if (yp >= iconsdown) { /* Full ? */ idone = 1; /* Note completed screen */ } } l1:; itemRef = derefItem(itemRef->sibling); } window->iconPtr->iconArray[icount].item = NULL; /* END MARKINGS */ if (_variableArray[30] == 0) { if (yp != 306) _variableArray[31] = 52; if ((xp == 188) && (yp == 358)) _variableArray[31] = 0; } addArrows(window, num); /* Plot arrows and add their boxes */ } void SimonEngine::addArrows(WindowBlock *window, uint num) { setArrowHitAreas(window, num); window->iconPtr->upArrow = _scrollUpHitArea; window->iconPtr->downArrow = _scrollDownHitArea; } void SimonEngine::setArrowHitAreas(WindowBlock *window, uint num) { HitArea *ha; ha = findEmptyHitArea(); _scrollUpHitArea = ha - _hitAreas; if (getGameType() == GType_FF) { ha->x = 496; ha->y = 279; ha->width = 30; ha->height = 45; ha->flags = kBFBoxInUse | kBFNoTouchName; ha->id = 0x7FFB; ha->priority = 100; ha->window = window; ha->verb = 1; } else if (getGameType() == GType_SIMON2) { ha->x = 81; ha->y = 158; ha->width = 12; ha->height = 26; ha->flags = kBFBoxInUse | kBFNoTouchName; ha->id = 0x7FFB; ha->priority = 100; ha->window = window; ha->verb = 1; } else { ha->x = 308; ha->y = 149; ha->width = 12; ha->height = 17; ha->flags = kBFBoxInUse | kBFNoTouchName; ha->id = 0x7FFB; ha->priority = 100; ha->window = window; ha->verb = 1; } ha = findEmptyHitArea(); _scrollDownHitArea = ha - _hitAreas; if (getGameType() == GType_FF) { ha->x = 496; ha->y = 324; ha->width = 30; ha->height = 44; ha->flags = kBFBoxInUse | kBFNoTouchName; ha->id = 0x7FFC; ha->priority = 100; ha->window = window; ha->verb = 1; } else if (getGameType() == GType_SIMON2) { ha->x = 227; ha->y = 162; ha->width = 12; ha->height = 26; ha->flags = kBFBoxInUse | kBFNoTouchName; ha->id = 0x7FFC; ha->priority = 100; ha->window = window; ha->verb = 1; } else { ha->x = 308; ha->y = 176; ha->width = 12; ha->height = 17; ha->flags = kBFBoxInUse | kBFNoTouchName; ha->id = 0x7FFC; ha->priority = 100; ha->window = window; ha->verb = 1; o_kill_sprite_simon1(128); loadSprite(0, 1, 128, 0, 0, 14); } } uint SimonEngine::setupIconHitArea(WindowBlock *window, uint num, uint x, uint y, Item *item_ptr) { HitArea *ha; ha = findEmptyHitArea(); if (getGameType() == GType_FF) { ha->x = x; ha->y = y; ha->item_ptr = item_ptr; ha->width = 45; ha->height = 44; ha->flags = kBFBoxInUse | kBFBoxItem; ha->id = num; ha->priority = 100; ha->verb = 208; } else if (getGameType() == GType_SIMON2) { ha->x = x + 110; ha->y = window->y + y; ha->item_ptr = item_ptr; ha->width = 20; ha->height = 20; ha->flags = kBFDragBox | kBFBoxInUse | kBFBoxItem; ha->id = 0x7FFD; ha->priority = 100; ha->verb = 208; } else { ha->x = (x + window->x) * 8; ha->y = y * 25 + window->y; ha->item_ptr = item_ptr; ha->width = 24; ha->height = 24; ha->flags = kBFDragBox | kBFBoxInUse | kBFBoxItem; ha->id = 0x7FFD; ha->priority = 100; ha->verb = 208; } return ha - _hitAreas; } void SimonEngine::removeIconArray(uint num) { WindowBlock *window; uint16 curWindow; uint16 i; window = _windowArray[num & 7]; curWindow = _curWindow; if (window == NULL || window->iconPtr == NULL) return; if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { changeWindow(num); windowPutChar(12); changeWindow(curWindow); } for (i = 0; window->iconPtr->iconArray[i].item != NULL; i++) { delete_hitarea_by_index(window->iconPtr->iconArray[i].boxCode); } if (window->iconPtr->upArrow != -1) { delete_hitarea_by_index(window->iconPtr->upArrow); } if (window->iconPtr->downArrow != -1) { delete_hitarea_by_index(window->iconPtr->downArrow); if (getGameType() == GType_SIMON1) removeArrows(window, num); } free(window->iconPtr); window->iconPtr = NULL; _fcsData1[num] = 0; _fcsData2[num] = 0; } void SimonEngine::removeArrows(WindowBlock *window, uint num) { o_kill_sprite_simon1(128); } } // End of namespace Simon