diff options
Diffstat (limited to 'engines/sword1/screen.cpp')
-rw-r--r-- | engines/sword1/screen.cpp | 539 |
1 files changed, 475 insertions, 64 deletions
diff --git a/engines/sword1/screen.cpp b/engines/sword1/screen.cpp index e5b1fcd5e5..3d184e15e5 100644 --- a/engines/sword1/screen.cpp +++ b/engines/sword1/screen.cpp @@ -34,6 +34,7 @@ #include "sword1/resman.h" #include "sword1/objectman.h" #include "sword1/menu.h" +#include "sword1/swordres.h" #include "sword1/sword1.h" #ifdef BACKEND_8BIT @@ -137,7 +138,7 @@ void Screen::fnSetPalette(uint8 start, uint16 length, uint32 id, bool fadeUp) { if (start == 0) // force color 0 to black palData[0] = palData[1] = palData[2] = 0; - if (SwordEngine::_systemVars.isMac) { // see bug #1701058 + if (SwordEngine::isMac()) { // see bug #1701058 if (start != 0 && start + length == 256) // and force color 255 to black as well palData[(length-1)*3+0] = palData[(length-1)*3+1] = palData[(length-1)*3+2] = 0; } @@ -358,24 +359,45 @@ void Screen::quitScreen(void) { void Screen::draw(void) { uint8 cnt; + + debug(8, "Screen::draw() -> _currentScreen %u", _currentScreen); + if (_currentScreen == 54) { // rm54 has a BACKGROUND parallax layer in parallax[0] - if (_parallax[0]) + if (_parallax[0] && !SwordEngine::isPsx() ) //Avoid drawing this parallax on PSX edition, it gets occluded by background renderParallax(_parallax[0]); uint8 *src = _layerBlocks[0]; uint8 *dest = _screenBuf; + uint8 *indxScreen = NULL; + + if(SwordEngine::isPsx()) { + indxScreen = psxShrinkedBackgroundToIndexed(_layerBlocks[0], _scrnSizeX, _scrnSizeY); + src = indxScreen; + } for (uint16 cnty = 0; cnty < _scrnSizeY; cnty++) for (uint16 cntx = 0; cntx < _scrnSizeX; cntx++) { if (*src) - if (!SwordEngine::_systemVars.isMac || *src != 255) // see bug #1701058 + if (!(SwordEngine::isMac()) || *src != 255) // see bug #1701058 *dest = *src; dest++; src++; } - } else + free(indxScreen); + + } else if (!(SwordEngine::isPsx())) { memcpy(_screenBuf, _layerBlocks[0], _scrnSizeX * _scrnSizeY); + } else { //We are using PSX version + uint8 *indxScreen; + if(_currentScreen == 45 || _currentScreen == 55 || + _currentScreen == 57 || _currentScreen == 63 || _currentScreen == 71) // Width shrinked backgrounds + indxScreen = psxShrinkedBackgroundToIndexed(_layerBlocks[0], _scrnSizeX, _scrnSizeY); + else + indxScreen = psxBackgroundToIndexed(_layerBlocks[0], _scrnSizeX, _scrnSizeY); + memcpy(_screenBuf, indxScreen, _scrnSizeX * _scrnSizeY); + free(indxScreen); + } for (cnt = 0; cnt < _backLength; cnt++) processImage(_backList[cnt]); @@ -393,6 +415,18 @@ void Screen::draw(void) { if (_parallax[1]) renderParallax(_parallax[1]); + // PSX version has parallax layer for this room in an external file (TRAIN.PLX) + if(SwordEngine::isPsx() && _currentScreen == 63) { + Common::File parallax; + uint8 *trainPLX = NULL; + parallax.open("TRAIN.PLX"); + trainPLX = (uint8*) malloc(parallax.size()); + parallax.read(trainPLX, parallax.size()); + parallax.close(); + renderParallax(trainPLX); + free(trainPLX); + } + for (cnt = 0; cnt < _foreLength; cnt++) processImage(_foreList[cnt]); @@ -403,8 +437,9 @@ void Screen::processImage(uint32 id) { Object *compact; FrameHeader *frameHead; int scale; - + compact = _objMan->fetchObject(id); + if (compact->o_type == TYPE_TEXT) frameHead = _textMan->giveSpriteData((uint8)compact->o_target); else @@ -414,6 +449,7 @@ void Screen::processImage(uint32 id) { uint16 spriteX = compact->o_anim_x; uint16 spriteY = compact->o_anim_y; + if (compact->o_status & STAT_SHRINK) { scale = (compact->o_scale_a * compact->o_ycoord + compact->o_scale_b) / 256; spriteX += ((int16)_resMan->readUint16(&frameHead->offsetX) * scale) / 256; @@ -425,7 +461,13 @@ void Screen::processImage(uint32 id) { } uint8 *tonyBuf = NULL; - if (frameHead->runTimeComp[3] == '7') { // RLE7 encoded? + uint8 *hifBuf = NULL; + if (SwordEngine::isPsx() && compact->o_type != TYPE_TEXT) { // PSX sprites are compressed with HIF + hifBuf = (uint8*)malloc(_resMan->readUint16(&frameHead->width) * _resMan->readUint16(&frameHead->height)/2); + memset(hifBuf, 0x00, (_resMan->readUint16(&frameHead->width) * _resMan->readUint16(&frameHead->height)/2)); + decompressHIF(sprData, hifBuf); + sprData = hifBuf; + } else if (frameHead->runTimeComp[3] == '7') { // RLE7 encoded? decompressRLE7(sprData, _resMan->readUint32(&frameHead->compSize), _rleBuffer); sprData = _rleBuffer; } else if (frameHead->runTimeComp[3] == '0') { // RLE0 encoded? @@ -439,14 +481,29 @@ void Screen::processImage(uint32 id) { uint16 sprSizeX, sprSizeY; if (compact->o_status & STAT_SHRINK) { - sprSizeX = (scale * _resMan->readUint16(&frameHead->width)) / 256; - sprSizeY = (scale * _resMan->readUint16(&frameHead->height)) / 256; - fastShrink(sprData, _resMan->readUint16(&frameHead->width), _resMan->readUint16(&frameHead->height), scale, _shrinkBuffer); + memset(_shrinkBuffer, 0, SHRINK_BUFFER_SIZE); //Clean shrink buffer to avoid corruption + if( SwordEngine::isPsx() && (compact->o_resource != GEORGE_MEGA)) { //PSX Height shrinked sprites + sprSizeX = (scale * _resMan->readUint16(&frameHead->width)) / 256; + sprSizeY = (scale * (_resMan->readUint16(&frameHead->height))) / 256 / 2; + fastShrink(sprData, _resMan->readUint16(&frameHead->width), (_resMan->readUint16(&frameHead->height)) / 2, scale, _shrinkBuffer); + } else if (SwordEngine::isPsx()) { //PSX width/height shrinked sprites + sprSizeX = (scale * _resMan->readUint16(&frameHead->width)) / 256 / 2; + sprSizeY = (scale * _resMan->readUint16(&frameHead->height)) / 256 / 2; + fastShrink(sprData, _resMan->readUint16(&frameHead->width) / 2, _resMan->readUint16(&frameHead->height) / 2, scale, _shrinkBuffer); + } else { + sprSizeX = (scale * _resMan->readUint16(&frameHead->width)) / 256; + sprSizeY = (scale * _resMan->readUint16(&frameHead->height)) / 256; + fastShrink(sprData, _resMan->readUint16(&frameHead->width), _resMan->readUint16(&frameHead->height), scale, _shrinkBuffer); + } sprData = _shrinkBuffer; } else { sprSizeX = _resMan->readUint16(&frameHead->width); - sprSizeY = _resMan->readUint16(&frameHead->height); + if(SwordEngine::isPsx()) { //PSX sprites are half height + sprSizeY = _resMan->readUint16(&frameHead->height) / 2; + } else + sprSizeY = (_resMan->readUint16(&frameHead->height)); } + if (!(compact->o_status & STAT_OVERRIDE)) { //mouse size linked to exact size & coordinates of sprite box - shrink friendly if (_resMan->readUint16(&frameHead->offsetX) || _resMan->readUint16(&frameHead->offsetY)) { @@ -463,24 +520,45 @@ void Screen::processImage(uint32 id) { compact->o_mouse_y2 = spriteY + sprSizeY; } } + uint16 sprPitch = sprSizeX; uint16 incr; spriteClipAndSet(&spriteX, &spriteY, &sprSizeX, &sprSizeY, &incr); + if ((sprSizeX > 0) && (sprSizeY > 0)) { - drawSprite(sprData + incr, spriteX, spriteY, sprSizeX, sprSizeY, sprPitch); - if (!(compact->o_status&STAT_FORE)) + if( (!(SwordEngine::isPsx()) || (compact->o_type == TYPE_TEXT) + || (compact->o_resource == LVSFLY) || !(compact->o_resource == GEORGE_MEGA) && (sprSizeX < 260))) + drawSprite(sprData + incr, spriteX, spriteY, sprSizeX, sprSizeY, sprPitch); + else if (((sprSizeX >= 260) && (sprSizeX < 450)) || ((compact->o_resource == GMWRITH) && (sprSizeX < 515)) // a psx shrinked sprite (1/2 width) + || ((compact->o_resource == GMPOWER) && (sprSizeX < 515)) ) // some needs to be hardcoded, headers don't give useful infos + drawPsxHalfShrinkedSprite(sprData + incr, spriteX, spriteY, sprSizeX / 2, sprSizeY, sprPitch / 2); + else if (sprSizeX >= 450) // A PSX double shrinked sprite (1/3 width) + drawPsxFullShrinkedSprite(sprData + incr, spriteX, spriteY, sprSizeX / 3, sprSizeY, sprPitch / 3); + else // This is for psx half shrinked, walking george and remaining sprites + drawPsxHalfShrinkedSprite(sprData + incr, spriteX, spriteY, sprSizeX, sprSizeY, sprPitch); + if (!(compact->o_status&STAT_FORE) && !(SwordEngine::isPsx() && (compact->o_resource == MOUBUSY))) // Check fixes moue sprite being masked by layer, happens only on psx verticalMask(spriteX, spriteY, sprSizeX, sprSizeY); } + if (compact->o_type != TYPE_TEXT) _resMan->resClose(compact->o_resource); + if (tonyBuf) free(tonyBuf); + + if (hifBuf) + free(hifBuf); } void Screen::verticalMask(uint16 x, uint16 y, uint16 bWidth, uint16 bHeight) { if (_roomDefTable[_currentScreen].totalLayers <= 1) return; + if (SwordEngine::isPsx()) { // PSX sprites are vertical shrinked, and some width shrinked + bHeight *= 2; + bWidth *= 2; + } + bWidth = (bWidth + (x & (SCRNGRID_X - 1)) + (SCRNGRID_X - 1)) / SCRNGRID_X; bHeight = (bHeight + (y & (SCRNGRID_Y - 1)) + (SCRNGRID_Y - 1)) / SCRNGRID_Y; @@ -504,7 +582,11 @@ void Screen::verticalMask(uint16 x, uint16 y, uint16 bWidth, uint16 bHeight) { uint16 *grid = _layerGrid[level] + gridX + blkx + gridY * lGridSizeX; for (int16 blky = bHeight - 1; blky >= 0; blky--) { if (*grid) { - uint8 *blkData = _layerBlocks[level + 1] + (_resMan->readUint16(grid) - 1) * 128; + uint8 *blkData; + if (SwordEngine::isPsx()) + blkData = _layerBlocks[level + 1] + (_resMan->readUint16(grid) - 1) * 64; //PSX layers are half height too... + else + blkData = _layerBlocks[level + 1] + (_resMan->readUint16(grid) - 1) * 128; blitBlockClear(x + blkx, y + blky, blkData); } else break; @@ -517,23 +599,45 @@ void Screen::verticalMask(uint16 x, uint16 y, uint16 bWidth, uint16 bHeight) { void Screen::blitBlockClear(uint16 x, uint16 y, uint8 *data) { uint8 *dest = _screenBuf + (y * SCRNGRID_Y) * _scrnSizeX + (x * SCRNGRID_X); - for (uint8 cnty = 0; cnty < SCRNGRID_Y; cnty++) { + + for (uint8 cnty = 0; cnty < (SwordEngine::isPsx() ? SCRNGRID_Y / 2 : SCRNGRID_Y); cnty++) { for (uint8 cntx = 0; cntx < SCRNGRID_X; cntx++) if (data[cntx]) dest[cntx] = data[cntx]; + + if (SwordEngine::isPsx()) { + dest += _scrnSizeX; + for (uint8 cntx = 0; cntx < SCRNGRID_X; cntx++) + if (data[cntx]) + dest[cntx] = data[cntx]; + } + data += SCRNGRID_X; dest += _scrnSizeX; } } void Screen::renderParallax(uint8 *data) { - ParallaxHeader *header = (ParallaxHeader*)data; - uint32 *lineIndexes = (uint32*)(data + sizeof(ParallaxHeader)); - assert((_resMan->getUint16(header->sizeX) >= SCREEN_WIDTH) && (_resMan->getUint16(header->sizeY) >= SCREEN_DEPTH)); - uint16 paraScrlX, paraScrlY; uint16 scrnScrlX, scrnScrlY; uint16 scrnWidth, scrnHeight; + uint16 paraSizeX, paraSizeY; + uint8 *psxPlx = NULL; + ParallaxHeader *header = NULL; + uint32 *lineIndexes = NULL; + + if (SwordEngine::isPsx()) { //Parallax headers are different in PSX version + psxPlx = psxParallaxToIndexed(data); + paraSizeX = READ_LE_UINT16(psxPlx); + paraSizeY = READ_LE_UINT16(psxPlx+2); + } else { + header = (ParallaxHeader*)data; + lineIndexes = (uint32*)(data + sizeof(ParallaxHeader)); + paraSizeX = _resMan->getUint16(header->sizeX); + paraSizeY = _resMan->getUint16(header->sizeY); + } + + assert((paraSizeX >= SCREEN_WIDTH) && (paraSizeY >= SCREEN_DEPTH)); // we have to render more than the visible screen part for displaying scroll frames scrnScrlX = MIN((uint32)_oldScrollX, Logic::_scriptVars[SCROLL_OFFSET_X]); @@ -541,71 +645,141 @@ void Screen::renderParallax(uint8 *data) { scrnScrlY = MIN((uint32)_oldScrollY, Logic::_scriptVars[SCROLL_OFFSET_Y]); scrnHeight = SCREEN_DEPTH + ABS((int32)_oldScrollY - (int32)Logic::_scriptVars[SCROLL_OFFSET_Y]); + if (_scrnSizeX != SCREEN_WIDTH) { - double scrlfx = (_resMan->getUint16(header->sizeX) - SCREEN_WIDTH) / ((double)(_scrnSizeX - SCREEN_WIDTH)); + double scrlfx = (paraSizeX - SCREEN_WIDTH) / ((double)(_scrnSizeX - SCREEN_WIDTH)); paraScrlX = (uint16)(scrnScrlX * scrlfx); } else paraScrlX = 0; if (_scrnSizeY != SCREEN_DEPTH) { - double scrlfy = (_resMan->getUint16(header->sizeY) - SCREEN_DEPTH) / ((double)(_scrnSizeY - SCREEN_DEPTH)); + double scrlfy = (paraSizeY - SCREEN_DEPTH) / ((double)(_scrnSizeY - SCREEN_DEPTH)); paraScrlY = (uint16)(scrnScrlY * scrlfy); } else paraScrlY = 0; - for (uint16 cnty = 0; cnty < scrnHeight; cnty++) { - uint8 *src = data + _resMan->readUint32(lineIndexes + cnty + paraScrlY); - uint8 *dest = _screenBuf + scrnScrlX + (cnty + scrnScrlY) * _scrnSizeX; - uint16 remain = paraScrlX; - uint16 xPos = 0; - while (remain) { // skip past the first part of the parallax to get to the right scrolling position - uint8 doSkip = *src++; - if (doSkip <= remain) - remain -= doSkip; - else { - xPos = doSkip - remain; - dest += xPos; - remain = 0; - } - uint8 doCopy = *src++; - if (doCopy <= remain) { - remain -= doCopy; - src += doCopy; - } else { - uint16 remCopy = doCopy - remain; - memcpy(dest, src + remain, remCopy); - dest += remCopy; - src += doCopy; - xPos = remCopy; - remain = 0; - } + if(SwordEngine::isPsx()) + for (uint16 cnty = 0; (cnty < SCREEN_DEPTH) && (cnty < paraSizeY); cnty++) { + uint8 *src = psxPlx + 4 + paraScrlY * paraSizeX + cnty * paraSizeX + paraScrlX; + uint8 *dest = _screenBuf + scrnScrlX + (cnty + scrnScrlY) * _scrnSizeX/* * 2*/; + uint8 pix; + for (uint16 idx = 0; (idx < SCREEN_WIDTH) && (idx < paraSizeX); idx++) // make sure we don't write outside screen + if (pix = *(src + idx)) //If data is 0x00, don't write (transparency) + *(dest + idx) = pix; } - while (xPos < scrnWidth) { - if (uint8 skip = *src++) { - dest += skip; - xPos += skip; - } - if (xPos < scrnWidth) { - if (uint8 doCopy = *src++) { - if (xPos + doCopy > scrnWidth) - doCopy = scrnWidth - xPos; - memcpy(dest, src, doCopy); - dest += doCopy; - xPos += doCopy; + else + for (uint16 cnty = 0; cnty < scrnHeight; cnty++) { + uint8 *src = data + _resMan->readUint32(lineIndexes + cnty + paraScrlY); + uint8 *dest = _screenBuf + scrnScrlX + (cnty + scrnScrlY) * _scrnSizeX; + uint16 remain = paraScrlX; + uint16 xPos = 0; + while (remain) { // skip past the first part of the parallax to get to the right scrolling position + uint8 doSkip = *src++; + if (doSkip <= remain) + remain -= doSkip; + else { + xPos = doSkip - remain; + dest += xPos; + remain = 0; + } + uint8 doCopy = *src++; + if (doCopy <= remain) { + remain -= doCopy; + src += doCopy; + } else { + uint16 remCopy = doCopy - remain; + memcpy(dest, src + remain, remCopy); + dest += remCopy; src += doCopy; + xPos = remCopy; + remain = 0; + } + } + while (xPos < scrnWidth) { + if (uint8 skip = *src++) { + dest += skip; + xPos += skip; + } + if (xPos < scrnWidth) { + if (uint8 doCopy = *src++) { + if (xPos + doCopy > scrnWidth) + doCopy = scrnWidth - xPos; + memcpy(dest, src, doCopy); + dest += doCopy; + xPos += doCopy; + src += doCopy; + } } } } - } + + if (psxPlx) + free(psxPlx); } void Screen::drawSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch) { uint8 *dest = _screenBuf + (sprY * _scrnSizeX) + sprX; - + for (uint16 cnty = 0; cnty < sprHeight; cnty++) { for (uint16 cntx = 0; cntx < sprWidth; cntx++) if (sprData[cntx]) dest[cntx] = sprData[cntx]; + + if (SwordEngine::isPsx()) { //On PSX version we need to double horizontal lines + dest += _scrnSizeX; + for (uint16 cntx = 0; cntx < sprWidth; cntx++) + if (sprData[cntx]) + dest[cntx] = sprData[cntx]; + } + + sprData += sprPitch; + dest += _scrnSizeX; + } +} + +// Used to draw psx sprites which are 1/2 of original width +void Screen::drawPsxHalfShrinkedSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch) { + uint8 *dest = _screenBuf + (sprY * _scrnSizeX) + sprX; + + for (uint16 cnty = 0; cnty < sprHeight; cnty++) { + for (uint16 cntx = 0; cntx < sprWidth; cntx++) + if (sprData[cntx]) { + dest[cntx * 2] = sprData[cntx]; //In these sprites we need to double vetical lines too... + dest[cntx * 2 + 1] = sprData[cntx]; + } + + dest += _scrnSizeX; + for (uint16 cntx = 0; cntx < sprWidth; cntx++) + if (sprData[cntx]) { + dest[cntx * 2] = sprData[cntx]; + dest[cntx * 2 + 1] = sprData[cntx]; + } + + sprData += sprPitch; + dest += _scrnSizeX; + } +} + +// Used to draw psx sprites which are 1/3 of original width +void Screen::drawPsxFullShrinkedSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch) { + uint8 *dest = _screenBuf + (sprY * _scrnSizeX) + sprX; + + for (uint16 cnty = 0; cnty < sprHeight; cnty++) { + for (uint16 cntx = 0; cntx < sprWidth ; cntx++) + if (sprData[cntx]) { + dest[cntx * 3] = sprData[cntx]; //In these sprites we need to double vertical lines too... + dest[cntx * 3 + 1] = sprData[cntx]; + dest[cntx * 3 + 2] = sprData[cntx]; + } + + dest += _scrnSizeX; + for (uint16 cntx = 0; cntx < sprWidth; cntx++) + if (sprData[cntx]) { + dest[cntx * 3] = sprData[cntx]; + dest[cntx * 3 + 1] = sprData[cntx]; + dest[cntx * 3 + 2] = sprData[cntx]; + } + sprData += sprPitch; dest += _scrnSizeX; } @@ -618,6 +792,7 @@ void Screen::fastShrink(uint8 *src, uint32 width, uint32 height, uint32 scale, u uint32 step = 0x10000 / scale; uint8 columnTab[160]; uint32 res = step >> 1; + for (uint16 cnt = 0; cnt < resWidth; cnt++) { columnTab[cnt] = (uint8)(res >> 8); res += step; @@ -675,6 +850,188 @@ void Screen::addToGraphicList(uint8 listId, uint32 objId) { } } +uint8* Screen::psxBackgroundToIndexed(uint8* psxBackground, uint32 bakXres, uint32 bakYres) { + uint32 xresInTiles = bakXres / 16; + uint32 yresInTiles = ((bakYres / 2) % 16) ? (bakYres / 32) + 1 : (bakYres / 32); + uint32 totTiles = xresInTiles * yresInTiles; + uint32 tileYpos = 0; //tile position in a virtual xresInTiles * yresInTiles grid + uint32 tileXpos = 0; + uint32 tag = READ_LE_UINT32(psxBackground); + + uint8 *decomp_tile = (uint8 *)malloc(16 * 16); //Tiles are always 16 * 16 + uint8 *halfres_buffer = (uint8 *)malloc(totTiles * 16 * 16); //This buffer will contain the half vertical res image + + bool isCompressed = (tag == 0x434F4D50); + + psxBackground += 4; //We skip the id tag + + for (uint32 currentTile = 0; currentTile < totTiles; currentTile++) { + uint32 tileOffset = READ_LE_UINT32(psxBackground + 4 * currentTile); + + if(isCompressed) + decompressHIF(psxBackground + tileOffset - 4, decomp_tile); //Decompress the tile into decomp_tile + else + memcpy(decomp_tile, psxBackground + tileOffset - 4, 16*16); + + if (currentTile > 0 && !(currentTile % xresInTiles)) { //Finished a line of tiles, going down + tileYpos++; + tileXpos = 0; + } + + for (byte tileLine=0; tileLine<16; tileLine++) + memcpy(halfres_buffer + tileLine * bakXres + tileXpos * 16 + tileYpos * bakXres * 16, decomp_tile + tileLine * 16, 16); //Copy data to destination buffer + + tileXpos++; + } + + free(decomp_tile); + + uint8 *fullres_buffer = (uint8 *)malloc(bakXres * yresInTiles * 32); + memset(fullres_buffer, 0x00, bakXres * yresInTiles * 32); + + //Let's linedouble the image (to keep correct aspect ratio) + for (uint32 currentLine = 0; currentLine < (bakYres/2); currentLine++) { + memcpy(fullres_buffer + currentLine * bakXres * 2, halfres_buffer + currentLine * bakXres, bakXres); // destination_line is 2*original_line + memcpy(fullres_buffer + currentLine * bakXres * 2 + bakXres, halfres_buffer + currentLine * bakXres, bakXres); // destination_line+1 + } + + free(halfres_buffer); + + return fullres_buffer; +} + +// needed because some psx backgrounds are half width and half height +uint8* Screen::psxShrinkedBackgroundToIndexed(uint8* psxBackground, uint32 bakXres, uint32 bakYres) { + uint32 xresInTiles = (bakXres / 2) % 16 ? (bakXres / 32) + 1 : (bakXres / 32); + uint32 yresInTiles = (bakYres / 2) % 16 ? (bakYres / 32) + 1 : (bakYres / 32); + uint32 totTiles = xresInTiles * yresInTiles; + uint32 tileYpos = 0; //tile position in a virtual xresInTiles * yresInTiles grid + uint32 tileXpos = 0; + uint32 dataBegin = READ_LE_UINT32(psxBackground + 4); + uint32 realWidth = xresInTiles * 16; + + uint8 *decomp_tile = (uint8 *)malloc(16 * 16); //Tiles are always 16 * 16 + uint8 *halfres_buffer = (uint8*) malloc(totTiles * 16 * 16); //This buffer will contain the half vertical res image + memset(halfres_buffer, 0, totTiles * 16 * 16); + + bool isCompressed = (READ_LE_UINT32(psxBackground) == MKID_BE('COMP')); + + totTiles -= xresInTiles; + psxBackground += 4; //We skip the id tag + + uint32 currentTile; + for (currentTile = 0; currentTile < totTiles; currentTile++) { + uint32 tileOffset = READ_LE_UINT32(psxBackground + 4 * currentTile); + + if(isCompressed) + decompressHIF(psxBackground + tileOffset - 4, decomp_tile); //Decompress the tile into decomp_tile + else + memcpy(decomp_tile, psxBackground + tileOffset - 4, 16 * 16); + + if (currentTile > 0 && !(currentTile % xresInTiles)) { //Finished a line of tiles, going down + tileYpos++; + tileXpos = 0; + } + + for (byte tileLine = 0; tileLine < 16; tileLine++) + memcpy(halfres_buffer + (tileLine * realWidth) + (tileXpos * 16) + (tileYpos * realWidth * 16), decomp_tile + (tileLine * 16), 16); //Copy data to destination buffer + + tileXpos++; + } + + uint8 *fullres_buffer = (uint8 *)malloc(bakXres * (yresInTiles + 1) * 32); + memset(fullres_buffer, 0x00, bakXres * (yresInTiles + 1) * 32); + + for (uint32 currentLine = 0; currentLine < ((yresInTiles - 1) * 16); currentLine++) { + for (uint32 cntx = 0; cntx < realWidth; cntx++) { + fullres_buffer[currentLine * 2 * bakXres + cntx * 2] = halfres_buffer[currentLine * realWidth + cntx]; + fullres_buffer[currentLine * 2 * bakXres + cntx * 2 + 1] = halfres_buffer[currentLine * realWidth + cntx]; + } + for (uint32 cntx = 0; cntx < realWidth; cntx++) { + fullres_buffer[(currentLine * 2 + 1) * bakXres + cntx * 2] = halfres_buffer[currentLine * realWidth + cntx]; + fullres_buffer[(currentLine * 2 + 1) * bakXres + cntx * 2 + 1] = halfres_buffer[currentLine * realWidth + cntx]; + } + } + free(halfres_buffer); + + //Calculate number of remaining tiles + uint32 remainingTiles = (dataBegin - (currentTile * 4 + 4)) / 4; + + // Last line of tiles is FULL WIDTH! + uint32 tileHeight = (remainingTiles == xresInTiles * 2) ? 16 : 8; + + halfres_buffer = (uint8*) malloc(bakXres * 16 * 2); + memset(halfres_buffer, 0, bakXres * 16 * 2); + + tileXpos = 0; + for (; currentTile < totTiles + remainingTiles; currentTile++) { + uint32 tileOffset = READ_LE_UINT32(psxBackground + 4 * currentTile); + + if(isCompressed) + decompressHIF(psxBackground + tileOffset - 4, decomp_tile); //Decompress the tile into decomp_tile + else + memcpy(decomp_tile, psxBackground + tileOffset - 4, 256); + + for (byte tileLine = 0; tileLine < tileHeight; tileLine++) + memcpy(halfres_buffer + tileLine * bakXres * 2 + tileXpos * 16, decomp_tile + tileLine * 16, 16); + + tileXpos++; + } + + free(decomp_tile); + + for (uint32 currentLine = 0; currentLine < tileHeight; currentLine++) { + memcpy(fullres_buffer + (currentLine + (yresInTiles - 1) * 16) * bakXres * 2, halfres_buffer + currentLine * bakXres * 2, bakXres * 2); + memcpy(fullres_buffer + (currentLine + (yresInTiles - 1) * 16) * bakXres * 2 + bakXres, halfres_buffer + currentLine * bakXres * 2, bakXres * 2); + } + + free(halfres_buffer); + + return fullres_buffer; +} + +uint8* Screen::psxParallaxToIndexed(uint8* psxParallax) { + uint16 xresInTiles = READ_LE_UINT16(psxParallax + 10); + uint16 yresInTiles = READ_LE_UINT16(psxParallax + 12); + uint16 totTiles = READ_LE_UINT16(psxParallax + 14); + uint32 plxXres = xresInTiles * 16; + uint32 plxYres = yresInTiles * 16; + + uint8 *plxPos = psxParallax + 16; + uint8 *plxOff = psxParallax + 16 + totTiles * 2; + uint8 *plxData = psxParallax + 16 + totTiles * 2 + totTiles * 4; + + uint8 *decomp_tile = (uint8 *)malloc(16 * 16); // Tiles are always 16 * 16 + uint8 *halfres_buffer = (uint8 *)malloc(4 + yresInTiles * xresInTiles * 16 * 16); //This buffer will contain the half vertical res image + memset(halfres_buffer, 0, 4 + yresInTiles * xresInTiles * 16 * 16); //Clean the buffer + + for (uint16 currentTile = 0; currentTile < totTiles - 1; currentTile++) { + uint32 tileOffset = READ_LE_UINT32(plxOff + 4 * currentTile); + uint8 tileXpos = *(plxPos + 2 * currentTile); //Fetch tile position in grid + uint8 tileYpos = *(plxPos + 2 * currentTile + 1); + decompressHIF(plxData + tileOffset, decomp_tile); //Decompress the tile into decomp_tile + + for (byte tileLine = 0; tileLine < 16; tileLine++) + memcpy(halfres_buffer + tileLine * plxXres + tileXpos * 16 + tileYpos * plxXres * 16, decomp_tile + tileLine * 16, 16); //Copy data to destination buffer + } + + free(decomp_tile); + + uint8 *dest_buffer = (uint8*) malloc (plxXres * plxYres * 2 + 4); + WRITE_LE_UINT16(dest_buffer, plxXres); //Insert resolution information + WRITE_LE_UINT16(dest_buffer + 2, plxYres*2); + + //Let's linedouble the image (to keep correct aspect ratio) + for (uint32 currentLine = 0; currentLine < plxYres; currentLine++) { + memcpy(dest_buffer + 4 + currentLine * plxXres * 2, halfres_buffer + currentLine * plxXres, plxXres); // destination_line is 2*original_line + memcpy(dest_buffer + 4 + currentLine * plxXres * 2 + plxXres, halfres_buffer + currentLine * plxXres, plxXres); // destination_line+1 + } + + free(halfres_buffer); + + return dest_buffer; +} + void Screen::decompressTony(uint8 *src, uint32 compSize, uint8 *dest) { uint8 *endOfData = src + compSize; while (src < endOfData) { @@ -721,6 +1078,30 @@ void Screen::decompressRLE0(uint8 *src, uint32 compSize, uint8 *dest) { } } +void Screen::decompressHIF(uint8 *src, uint8 *dest) { + for (;;) { //Main loop + byte control_byte = *src++; + uint32 byte_count = 0; + while (byte_count < 8) { + if (control_byte & 0x80) { + uint16 info_word = READ_BE_UINT16(src); //Read the info word + src += 2; + if (info_word == 0xFFFF) return; //Got 0xFFFF code, finished. + + int32 repeat_count = (info_word >> 12) + 2; //How many time data needs to be refetched + while(repeat_count >= 0) { + uint8 *old_data_src = dest - ((info_word & 0xFFF) + 1); + *dest++ = *old_data_src; + repeat_count--; + } + } else + *dest++ = *src++; + byte_count++; + control_byte <<= 1; //Shifting left the control code one bit + } + } +} + void Screen::fadePalette(void) { if (_fadingStep == 16) memcpy(_currentPalette, _targetPalette, 256 * 4); @@ -773,6 +1154,7 @@ void Screen::spriteClipAndSet(uint16 *pSprX, uint16 *pSprY, uint16 *pSprWidth, u *pSprWidth = 0; else *pSprWidth = (uint16)sprW; + *pSprX = (uint16)sprX; *pSprY = (uint16)sprY; @@ -780,6 +1162,19 @@ void Screen::spriteClipAndSet(uint16 *pSprX, uint16 *pSprY, uint16 *pSprWidth, u // sprite will be drawn, so mark it in the grid buffer uint16 gridH = (*pSprHeight + (sprY & (SCRNGRID_Y - 1)) + (SCRNGRID_Y - 1)) / SCRNGRID_Y; uint16 gridW = (*pSprWidth + (sprX & (SCRNGRID_X - 1)) + (SCRNGRID_X - 1)) / SCRNGRID_X; + + if(SwordEngine::isPsx()) { + gridH *= 2; // This will correct the PSX sprite being cut at half height + gridW *= 2; // and masking problems when sprites are stretched in width + + uint16 bottomSprPos = (*pSprY + (*pSprHeight) * 2); //Position of bottom line of sprite + if ( bottomSprPos > _scrnSizeY ) { //Check that resized psx sprite isn't drawn outside of screen boundaries + uint16 outScreen = bottomSprPos - _scrnSizeY; + *pSprHeight -= (outScreen % 2) ? (outScreen + 1) / 2 : outScreen / 2; + } + + } + uint16 gridX = sprX / SCRNGRID_X; uint16 gridY = sprY / SCRNGRID_Y; uint8 *gridBuf = _screenGrid + gridX + gridY * _gridSizeX; @@ -806,16 +1201,32 @@ void Screen::showFrame(uint16 x, uint16 y, uint32 resId, uint32 frameNo, const b uint8 frame[40 * 40]; int i, j; - memset(frame, 199, sizeof(frame)); // Dark gray background + if(SwordEngine::isPsx()) + memset(frame, 0, sizeof(frame)); // PSX top menu is black + else + memset(frame, 199, sizeof(frame)); // Dark gray background if (resId != 0xffffffff) { FrameHeader *frameHead = _resMan->fetchFrame(_resMan->openFetchRes(resId), frameNo); uint8 *frameData = ((uint8*)frameHead) + sizeof(FrameHeader); - for (i = 0; i < _resMan->getUint16(frameHead->height); i++) { - for (j = 0; j < _resMan->getUint16(frameHead->height); j++) { - frame[(i + 4) * 40 + j + 2] = frameData[i * _resMan->getUint16(frameHead->width) + j]; + if (SwordEngine::isPsx()) { //We need to decompress PSX frames + uint8 *frameBufferPSX = (uint8 *)malloc(_resMan->getUint16(frameHead->width) * _resMan->getUint16(frameHead->height)/2); + decompressHIF(frameData, frameBufferPSX); + + for (i = 0; i < _resMan->getUint16(frameHead->height) / 2; i++) { + for (j = 0; j < _resMan->getUint16(frameHead->width); j++) { + uint8 data = frameBufferPSX[i * _resMan->getUint16(frameHead->width) + j]; + frame[(i * 2 + 4) * 40 + j + 2] = data; + frame[(i * 2 + 1 + 4) * 40 + j + 2] = data; //Linedoubling the sprite + } } + + free(frameBufferPSX); + } else { + for (i = 0; i < _resMan->getUint16(frameHead->height); i++) + for (j = 0; j < _resMan->getUint16(frameHead->height); j++) + frame[(i + 4) * 40 + j + 2] = frameData[i * _resMan->getUint16(frameHead->width) + j]; } _resMan->resClose(resId); |