mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-13 07:14:59 +00:00
SWORD1: PSX: Implement credits
The PSX version doesn't have a credits Smacker video but has a CREDITS.DAT file containing credits strings which have to be displayed as a scrolling list of names and roles.
This commit is contained in:
parent
e19ac44440
commit
5daae32032
@ -3312,4 +3312,374 @@ const uint8 Control::_mediaHouseLanguageStrings[20][43] = {
|
||||
"DRIVE FULL!",
|
||||
};
|
||||
|
||||
/* ---------- PSX CREDITS CODE ---------- */
|
||||
|
||||
int32 Control::getCreditsStringLength(uint8 *str, uint8 *font) {
|
||||
int32 width = 0;
|
||||
FrameHeader *f;
|
||||
|
||||
while (*str) {
|
||||
f = (FrameHeader *)_resMan->fetchFrame(font, *str - 32);
|
||||
width += f->width;
|
||||
str++;
|
||||
|
||||
if (*str)
|
||||
width += PSX_CREDITS_SPACING;
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
int32 Control::getCreditsFontHeight(uint8 *font) {
|
||||
FrameHeader *f;
|
||||
|
||||
f = (FrameHeader *)_resMan->fetchFrame(font, 'A' - 32);
|
||||
return (f->height / 2);
|
||||
}
|
||||
|
||||
void Control::createCreditsTextSprite(uint8 *data, int32 pitch, uint8 *str, uint8 *font) {
|
||||
uint16 x = 0;
|
||||
FrameHeader *f;
|
||||
uint8 *src, *dst;
|
||||
|
||||
while (*str) {
|
||||
f = (FrameHeader *)_resMan->fetchFrame(font, *str - 32);
|
||||
|
||||
src = (uint8 *)f + sizeof(FrameHeader);
|
||||
dst = data + x;
|
||||
|
||||
for (int i = 0; i < f->height / 2; i++) {
|
||||
memcpy(dst, src, f->width);
|
||||
src += f->width;
|
||||
dst += pitch;
|
||||
}
|
||||
|
||||
x += (f->width + PSX_CREDITS_SPACING);
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
void Control::renderCreditsTextSprite(uint8 *data, uint8 *screenBuf, int16 x, int16 y, int16 width, int16 height) {
|
||||
// Coordinates corrections from disasm
|
||||
// (remember that the PSX framebuffer is bigger than our target 640x480 screen)
|
||||
x -= 129;
|
||||
y -= 72;
|
||||
|
||||
// Boundary checks
|
||||
if (x >= SCREEN_WIDTH || y >= SCREEN_FULL_DEPTH)
|
||||
return;
|
||||
|
||||
if (x + width <= 0 || y + height <= 0)
|
||||
return;
|
||||
|
||||
// Are there rows outside the screen?
|
||||
// Calculate how many doubled rows of the sprite are outside the screen on the top
|
||||
int16 skippedDoubledRows = (y < 0) ? -y : 0;
|
||||
int16 skippedRowsInData = skippedDoubledRows / 2;
|
||||
|
||||
data += width * skippedRowsInData; // Adjust data pointer based on the number of skipped rows in the sprite
|
||||
height -= skippedDoubledRows; // Adjust height based on the number of skipped doubled rows
|
||||
|
||||
if (y < 0) {
|
||||
y = 0;
|
||||
}
|
||||
|
||||
uint8 *dst = screenBuf + x + SCREEN_WIDTH * y;
|
||||
|
||||
for (int i = 0; i < height; i += 2) { // Increment by 2 for the doubled height
|
||||
// Boundary check for y
|
||||
if (y + i >= SCREEN_FULL_DEPTH)
|
||||
break;
|
||||
|
||||
// First horizontal line
|
||||
for (int j = 0; j < width; j++) {
|
||||
// Boundary checks for x
|
||||
if (x + j < 0)
|
||||
continue;
|
||||
|
||||
if (x + j >= SCREEN_WIDTH)
|
||||
break;
|
||||
|
||||
if (data[j])
|
||||
dst[j] = data[j];
|
||||
}
|
||||
|
||||
dst += SCREEN_WIDTH;
|
||||
|
||||
// Second horizontal line (duplicated)
|
||||
for (int j = 0; j < width; j++) {
|
||||
// Boundary checks for x
|
||||
if (x + j < 0)
|
||||
continue;
|
||||
|
||||
if (x + j >= SCREEN_WIDTH)
|
||||
break;
|
||||
|
||||
if (data[j])
|
||||
dst[j] = data[j];
|
||||
}
|
||||
|
||||
dst += SCREEN_WIDTH; // Move to the next line
|
||||
data += width; // Move to the next row of source sprite
|
||||
}
|
||||
}
|
||||
|
||||
void Control::psxEndCredits() {
|
||||
int16 h;
|
||||
int16 nextCredit = PSX_NUM_CREDITS + 1;
|
||||
uint8 *creditLine = nullptr;
|
||||
uint8 *titleLine = nullptr;
|
||||
int32 *creditData = nullptr;
|
||||
Common::File creditsFile;
|
||||
|
||||
int32 creditsFileSize = 0;
|
||||
int32 totalCreditsNum = 0;
|
||||
|
||||
uint8 *creditSprite[PSX_NUM_CREDITS];
|
||||
uint8 *titleSprite[PSX_NUM_CREDITS];
|
||||
int16 creditWidth[PSX_NUM_CREDITS];
|
||||
int16 titleWidth[PSX_NUM_CREDITS];
|
||||
int16 creditsHeight[PSX_NUM_CREDITS] = {
|
||||
400, 440, 480, 520, 560, 600, 640,
|
||||
680, 720, 760, 800, 840, 880, 920
|
||||
};
|
||||
|
||||
for (int i = 0; i < PSX_NUM_CREDITS; i++) {
|
||||
creditSprite[i] = nullptr;
|
||||
titleSprite[i] = nullptr;
|
||||
creditWidth[i] = 0;
|
||||
titleWidth[i] = 0;
|
||||
}
|
||||
|
||||
// If we're here, the resource is already there, no need to open it
|
||||
uint8 *font = (uint8 *)_resMan->fetchRes(GAME_FONT);
|
||||
|
||||
switch (SwordEngine::_systemVars.language) {
|
||||
case BS1_ENGLISH:
|
||||
totalCreditsNum = 101;
|
||||
creditsFileSize = 2798;
|
||||
break;
|
||||
case BS1_GERMAN:
|
||||
totalCreditsNum = 83;
|
||||
creditsFileSize = 2382;
|
||||
break;
|
||||
case BS1_FRENCH:
|
||||
totalCreditsNum = 83;
|
||||
creditsFileSize = 2382;
|
||||
break;
|
||||
case BS1_SPANISH:
|
||||
totalCreditsNum = 83;
|
||||
creditsFileSize = 2412;
|
||||
break;
|
||||
case BS1_ITALIAN:
|
||||
totalCreditsNum = 101;
|
||||
creditsFileSize = 2823;
|
||||
break;
|
||||
default:
|
||||
totalCreditsNum = 101;
|
||||
creditsFileSize = 2798;
|
||||
}
|
||||
|
||||
_sound->clearAllFx();
|
||||
_screen->startFadePaletteUp(1);
|
||||
|
||||
for (int i = 0; i < PSX_NUM_CREDITS; i++)
|
||||
creditsHeight[i] = 400 + i * 40;
|
||||
|
||||
h = getCreditsFontHeight(font);
|
||||
_screen->fnSetFadeTargetPalette(193, 1, 0, TEXT_WHITE);
|
||||
|
||||
_sound->streamMusicFile(101, 1);
|
||||
_sound->updateMusicStreaming();
|
||||
|
||||
uint8 *creditsScreenBuf = (uint8 *)malloc(SCREEN_WIDTH * SCREEN_FULL_DEPTH);
|
||||
if (!creditsScreenBuf) {
|
||||
warning("Control::psxEndCredits(): Couldn't allocate memory for credits screen buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(creditsScreenBuf, 0, SCREEN_WIDTH * SCREEN_FULL_DEPTH);
|
||||
|
||||
creditData = (int32 *)malloc(creditsFileSize);
|
||||
if (!creditData) {
|
||||
warning("Control::psxEndCredits(): Couldn't allocate memory for text data");
|
||||
free(creditsScreenBuf);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!creditsFile.exists("CREDITS.DAT")) {
|
||||
debug(2, "Control::psxEndCredits(): Couldn't find CREDITS.DAT");
|
||||
free(creditsScreenBuf);
|
||||
free(creditData);
|
||||
return;
|
||||
}
|
||||
|
||||
creditsFile.open("CREDITS.DAT");
|
||||
if (!creditsFile.isOpen()) {
|
||||
debug(2, "Control::psxEndCredits(): Couldn't open CREDITS.DAT");
|
||||
free(creditsScreenBuf);
|
||||
free(creditData);
|
||||
return;
|
||||
}
|
||||
|
||||
creditsFile.read(creditData, creditsFileSize);
|
||||
creditsFile.close();
|
||||
|
||||
bool allSet = true;
|
||||
|
||||
for (int i = 0; i < PSX_NUM_CREDITS; i++) {
|
||||
_sound->updateMusicStreaming();
|
||||
_sound->setCrossFadeIncrement();
|
||||
|
||||
creditLine = ((uint8 *)creditData + creditData[i + totalCreditsNum]);
|
||||
titleLine = ((uint8 *)creditData + creditData[i]);
|
||||
|
||||
creditWidth[i] = (getCreditsStringLength(creditLine, font) + 1) & 0xFFFE;
|
||||
titleWidth[i] = (getCreditsStringLength(titleLine, font) + 1) & 0xFFFE;
|
||||
|
||||
if (creditWidth[i]) {
|
||||
creditSprite[i] = (uint8 *)malloc(h * creditWidth[i]);
|
||||
if (!creditSprite[i]) {
|
||||
warning("Control::psxEndCredits(): Couldn't allocate memory for text sprites");
|
||||
allSet = false;
|
||||
break; // Break so the clean-up code is executed
|
||||
}
|
||||
|
||||
memset(creditSprite[i], 0, h * creditWidth[i]);
|
||||
} else {
|
||||
creditSprite[i] = nullptr;
|
||||
}
|
||||
|
||||
if (titleWidth[i]) {
|
||||
titleSprite[i] = (uint8 *)malloc(h * titleWidth[i]);
|
||||
if (!titleSprite[i]) {
|
||||
warning("Control::psxEndCredits(): Couldn't allocate memory for text sprites");
|
||||
allSet = false;
|
||||
break; // Break so the clean-up code is executed
|
||||
}
|
||||
|
||||
memset(titleSprite[i], 0, h * titleWidth[i]);
|
||||
} else {
|
||||
titleSprite[i] = nullptr;
|
||||
}
|
||||
|
||||
createCreditsTextSprite(creditSprite[i], creditWidth[i], creditLine, font);
|
||||
createCreditsTextSprite(titleSprite[i], titleWidth[i], titleLine, font);
|
||||
}
|
||||
|
||||
_keyPressed.reset();
|
||||
|
||||
while (allSet && creditsHeight[PSX_NUM_CREDITS - 1] > -120 &&
|
||||
!Engine::shouldQuit() &&
|
||||
_keyPressed.keycode != Common::KEYCODE_ESCAPE) {
|
||||
memset(creditsScreenBuf, 0, SCREEN_WIDTH * SCREEN_FULL_DEPTH);
|
||||
|
||||
for (int i = 0; i < PSX_NUM_CREDITS; i++) {
|
||||
// Name
|
||||
renderCreditsTextSprite(
|
||||
creditSprite[i],
|
||||
creditsScreenBuf,
|
||||
PSX_CREDITS_MIDDLE + Logic::_scriptVars[SCROLL_OFFSET_X],
|
||||
PSX_CREDITS_OFFSET + creditsHeight[i],
|
||||
creditWidth[i],
|
||||
h * 2);
|
||||
|
||||
// Role
|
||||
renderCreditsTextSprite(
|
||||
titleSprite[i],
|
||||
creditsScreenBuf,
|
||||
PSX_CREDITS_MIDDLE + Logic::_scriptVars[SCROLL_OFFSET_X] - 30 - titleWidth[i],
|
||||
PSX_CREDITS_OFFSET + creditsHeight[i],
|
||||
titleWidth[i],
|
||||
h * 2);
|
||||
|
||||
creditsHeight[i] -= 2;
|
||||
}
|
||||
|
||||
_system->copyRectToScreen(creditsScreenBuf, SCREEN_WIDTH, 0, 0, SCREEN_WIDTH, SCREEN_FULL_DEPTH);
|
||||
|
||||
delay(33); // Run credits at about 30 FPS
|
||||
|
||||
// Always remember to update sound :-)
|
||||
_sound->updateMusicStreaming();
|
||||
_sound->setCrossFadeIncrement();
|
||||
|
||||
// Scroll the credits!
|
||||
if (creditsHeight[0] < -120) {
|
||||
if (nextCredit <= totalCreditsNum) {
|
||||
for (int i = 0; i < PSX_NUM_CREDITS; i++) {
|
||||
creditsHeight[i] += 40;
|
||||
}
|
||||
|
||||
if (creditSprite[0] != nullptr)
|
||||
free(creditSprite[0]);
|
||||
|
||||
if (titleSprite[0] != nullptr)
|
||||
free(titleSprite[0]);
|
||||
|
||||
for (int i = 0; i < PSX_NUM_CREDITS - 1; i++) {
|
||||
creditSprite[i] = creditSprite[i + 1];
|
||||
titleSprite[i] = titleSprite[i + 1];
|
||||
creditWidth[i] = creditWidth[i + 1];
|
||||
titleWidth[i] = titleWidth[i + 1];
|
||||
}
|
||||
|
||||
creditLine = ((uint8 *)creditData + creditData[nextCredit - 1 + totalCreditsNum]);
|
||||
titleLine = ((uint8 *)creditData + creditData[nextCredit - 1]);
|
||||
|
||||
creditWidth[PSX_NUM_CREDITS - 1] = (getCreditsStringLength(creditLine, font) + 1) & 0xFFFE;
|
||||
titleWidth[PSX_NUM_CREDITS - 1] = (getCreditsStringLength(titleLine, font) + 1) & 0xFFFE;
|
||||
|
||||
if (creditWidth[PSX_NUM_CREDITS - 1]) {
|
||||
creditSprite[PSX_NUM_CREDITS - 1] = (uint8 *)malloc(h * creditWidth[PSX_NUM_CREDITS - 1]);
|
||||
if (!creditSprite[PSX_NUM_CREDITS - 1]) {
|
||||
warning("Control::psxEndCredits(): Couldn't allocate memory for text sprites");
|
||||
break;
|
||||
}
|
||||
|
||||
memset(creditSprite[PSX_NUM_CREDITS - 1], 0, h * creditWidth[PSX_NUM_CREDITS - 1]);
|
||||
} else {
|
||||
creditSprite[PSX_NUM_CREDITS - 1] = nullptr;
|
||||
}
|
||||
|
||||
if (titleWidth[PSX_NUM_CREDITS - 1]) {
|
||||
titleSprite[PSX_NUM_CREDITS - 1] = (uint8 *)malloc(h * titleWidth[PSX_NUM_CREDITS - 1]);
|
||||
if (!titleSprite[PSX_NUM_CREDITS - 1]) {
|
||||
warning("Control::psxEndCredits(): Couldn't allocate memory for text sprites");
|
||||
break;
|
||||
}
|
||||
|
||||
memset(titleSprite[PSX_NUM_CREDITS - 1], 0, h * titleWidth[PSX_NUM_CREDITS - 1]);
|
||||
} else {
|
||||
titleSprite[PSX_NUM_CREDITS - 1] = nullptr;
|
||||
}
|
||||
|
||||
createCreditsTextSprite(creditSprite[PSX_NUM_CREDITS - 1], creditWidth[PSX_NUM_CREDITS - 1], creditLine, font);
|
||||
createCreditsTextSprite(titleSprite[PSX_NUM_CREDITS - 1], titleWidth[PSX_NUM_CREDITS - 1], titleLine, font);
|
||||
|
||||
nextCredit += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < PSX_NUM_CREDITS; i++) {
|
||||
if (creditSprite[i] != nullptr)
|
||||
free(creditSprite[i]);
|
||||
|
||||
if (titleSprite[i] != nullptr)
|
||||
free(titleSprite[i]);
|
||||
}
|
||||
|
||||
free(creditData);
|
||||
|
||||
_screen->startFadePaletteDown(1);
|
||||
_vm->waitForFade();
|
||||
|
||||
memset(creditsScreenBuf, 0, SCREEN_WIDTH * SCREEN_FULL_DEPTH);
|
||||
_system->copyRectToScreen(creditsScreenBuf, SCREEN_WIDTH, 0, 0, SCREEN_WIDTH, SCREEN_FULL_DEPTH);
|
||||
free(creditsScreenBuf);
|
||||
|
||||
_keyPressed.reset();
|
||||
}
|
||||
|
||||
} // End of namespace Sword1
|
||||
|
@ -105,6 +105,11 @@ class Logic;
|
||||
#define SP_OVERLAP 2
|
||||
#define TEXTBUTTONID 7
|
||||
|
||||
#define PSX_CREDITS_SPACING (-3)
|
||||
#define PSX_CREDITS_MIDDLE 450
|
||||
#define PSX_CREDITS_OFFSET 150
|
||||
#define PSX_NUM_CREDITS 14
|
||||
|
||||
struct Button {
|
||||
int32 x1;
|
||||
int32 y1;
|
||||
@ -126,6 +131,7 @@ public:
|
||||
void checkForOldSaveGames();
|
||||
bool isPanelShown();
|
||||
const uint8 *getPauseString();
|
||||
void psxEndCredits();
|
||||
|
||||
void setSaveDescription(int slot, const char *desc) {
|
||||
Common::strcpy_s((char *)_fileDescriptions[slot], sizeof(_fileDescriptions[slot]), desc);
|
||||
@ -198,6 +204,12 @@ private:
|
||||
|
||||
int displayMessage(const char *altButton, MSVC_PRINTF const char *message, ...) GCC_PRINTF(3, 4);
|
||||
|
||||
// PSX Credits functions
|
||||
int32 getCreditsFontHeight(uint8 *font);
|
||||
int32 getCreditsStringLength(uint8 *str, uint8 *font);
|
||||
void renderCreditsTextSprite(uint8 *data, uint8 *dst, int16 x, int16 y, int16 width, int16 height);
|
||||
void createCreditsTextSprite(uint8 *data, int32 pitch, uint8 *str, uint8 *font);
|
||||
|
||||
Common::MemoryWriteStreamDynamic *_tempThumbnail;
|
||||
static const uint8 _languageStrings[8 * 20][43];
|
||||
static const uint8 _akellaLanguageStrings[20][43];
|
||||
|
@ -95,6 +95,10 @@ void Logic::initialize() {
|
||||
SwordEngine::_systemVars.speechFinished = true;
|
||||
}
|
||||
|
||||
void Logic::setControlPanelObject(Control *control) {
|
||||
_control = control;
|
||||
}
|
||||
|
||||
void Logic::newScreen(uint32 screen) {
|
||||
Object *compact = (Object *)_objMan->fetchObject(PLAYER);
|
||||
|
||||
@ -984,21 +988,26 @@ int Logic::fnPlaySequence(Object *cpt, int32 id, int32 sequenceId, int32 d, int3
|
||||
// meantime, we don't want any looping sound effects still playing.
|
||||
_sound->clearAllFx();
|
||||
|
||||
MoviePlayer *player = makeMoviePlayer(sequenceId, _vm, _textMan, _resMan, _sound, _system);
|
||||
if (player) {
|
||||
_screen->clearScreen();
|
||||
if (player->load(sequenceId))
|
||||
player->play();
|
||||
delete player;
|
||||
if (SwordEngine::isPsx() && sequenceId == 19) {
|
||||
_control->psxEndCredits();
|
||||
} else {
|
||||
MoviePlayer *player = makeMoviePlayer(sequenceId, _vm, _textMan, _resMan, _sound, _system);
|
||||
if (player) {
|
||||
_screen->clearScreen();
|
||||
if (player->load(sequenceId))
|
||||
player->play();
|
||||
delete player;
|
||||
|
||||
// In some instances, when you start a video when the palette is still fading
|
||||
// and the video is finished earlier, another palette fade(-out) is performed with the
|
||||
// wrong palette. This happens when traveling to Spain or Ireland. It couldn't happen
|
||||
// in the original, as it asked for the CD before loading the scene.
|
||||
// Let's fix this by forcing a black fade palette on the next fade out. If a fade-in
|
||||
// is then scheduled, we will clear the flag without doing anything different from the usual.
|
||||
_screen->setNextFadeOutToBlack();
|
||||
// In some instances, when you start a video when the palette is still fading
|
||||
// and the video is finished earlier, another palette fade(-out) is performed with the
|
||||
// wrong palette. This happens when traveling to Spain or Ireland. It couldn't happen
|
||||
// in the original, as it asked for the CD before loading the scene.
|
||||
// Let's fix this by forcing a black fade palette on the next fade out. If a fade-in
|
||||
// is then scheduled, we will clear the flag without doing anything different from the usual.
|
||||
_screen->setNextFadeOutToBlack();
|
||||
}
|
||||
}
|
||||
|
||||
return SCRIPT_CONT;
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,7 @@ class Menu;
|
||||
class Router;
|
||||
class Screen;
|
||||
class Mouse;
|
||||
class Control;
|
||||
|
||||
class Logic;
|
||||
typedef int (Logic::*BSMcodeTable)(Object *, int32, int32, int32, int32, int32, int32, int32);
|
||||
@ -57,6 +58,7 @@ public:
|
||||
Logic(SwordEngine *vm, ObjectMan *pObjMan, ResMan *resMan, Screen *pScreen, Mouse *pMouse, Sound *pSound, Menu *pMenu, OSystem *system, Audio::Mixer *mixer);
|
||||
~Logic();
|
||||
void initialize();
|
||||
void setControlPanelObject(Control *control);
|
||||
void newScreen(uint32 screen);
|
||||
void engine();
|
||||
void updateScreenParams();
|
||||
@ -81,6 +83,7 @@ private:
|
||||
Text *_textMan;
|
||||
EventManager *_eventMan;
|
||||
Menu *_menu;
|
||||
Control *_control;
|
||||
uint32 _newScript; // <= ugly, but I can't avoid it.
|
||||
uint8 _speechClickDelay = 0;
|
||||
Common::RandomSource _rnd;
|
||||
|
@ -172,6 +172,7 @@ Common::Error SwordEngine::init() {
|
||||
_objectMan->initialize();
|
||||
_mouse->initialize();
|
||||
_control = new Control(this, _saveFileMan, _resMan, _objectMan, _system, _mouse, _sound, _screen, _logic);
|
||||
_logic->setControlPanelObject(_control);
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user