M4: Fixes for crashes in Load menu

This commit is contained in:
Paul Gilbert 2024-01-17 22:17:38 -08:00
parent c146fbb42f
commit 072355f870
6 changed files with 114 additions and 122 deletions

View File

@ -19,6 +19,7 @@
*
*/
#include "graphics/thumbnail.h"
#include "m4/burger/gui/game_menu.h"
#include "m4/burger/gui/interface.h"
#include "m4/adv_r/other.h"
@ -53,16 +54,15 @@ void CreateSaveMenu(RGB8 *myPalette);
void CreateLoadMenu(RGB8 *myPalette);
Sprite *menu_CreateThumbnail(int32 *spriteSize) {
#ifdef TODO
Sprite *thumbNailSprite;
GrBuff *thumbNail;
Buffer *scrnBuff, *intrBuff, *destBuff, RLE8Buff;
Buffer *scrnBuff, *intrBuff, *destBuff;
uint8 *srcPtr, *srcPtr2, *srcPtr3, *srcRowPtr, *destPtr;
ScreenContext *gameScreen;
int32 i, status;
int32 currRow, beginRow, endRow;
// Create a Sprite for the rle8 thumbNail
// Create a Sprite for the thumbNail
if ((thumbNailSprite = (Sprite *)mem_alloc(sizeof(Sprite), "sprite")) == nullptr) {
return nullptr;
}
@ -204,27 +204,16 @@ Sprite *menu_CreateThumbnail(int32 *spriteSize) {
memset(destPtr, 21, (destBuff->h - (currRow / 3)) * destBuff->stride);
}
// Compress the thumbNail data into the RLE8Buff
if ((*spriteSize = (int32)gr_sprite_RLE8_encode(destBuff, &RLE8Buff)) <= 0) {
return nullptr;
}
// Fill in the Sprite structure
thumbNailSprite->w = destBuff->w;
thumbNailSprite->h = destBuff->h;
thumbNailSprite->encoding = RLE8;
thumbNailSprite->data = nullptr;
thumbNailSprite->encoding = NO_COMPRESS;
thumbNailSprite->data = destBuff->data;
if ((thumbNailSprite->sourceHandle = NewHandle(*spriteSize, "thumbNail source")) == nullptr) {
return nullptr;
}
thumbNailSprite->sourceOffset = 0;
// Now copy the RLE8Buff into the thumbNail source handle
HLock(thumbNailSprite->sourceHandle);
thumbNailSprite->data = (uint8 *)(*(thumbNailSprite->sourceHandle));
memcpy(thumbNailSprite->data, RLE8Buff.data, *spriteSize);
HUnLock(thumbNailSprite->sourceHandle);
// Release all buffers
_G(gameDrawBuff)->release();
if (intrBuff) {
@ -232,14 +221,10 @@ Sprite *menu_CreateThumbnail(int32 *spriteSize) {
}
thumbNail->release();
// Free up both the thumbNail and the RLE8Buff
// Free up the thumbNail
delete thumbNail;
mem_free((void *)RLE8Buff.data);
return thumbNailSprite;
#else
error("TODO: createThumbnail");
#endif
}
@ -482,9 +467,7 @@ void menu_DrawButton(void *theItem, void *theMenu, int32 x, int32 y, int32, int3
myButton = (menuItemButton *)myItem->itemInfo;
switch (myButton->buttonType) {
case BTN_TYPE_GM_GENERIC:
switch (myButton->itemFlags) {
case BTN_STATE_NORM:
mySprite = _GM(menuSprites)[GM_BUTTON_NORM];
@ -503,7 +486,6 @@ void menu_DrawButton(void *theItem, void *theMenu, int32 x, int32 y, int32, int3
break;
case BTN_TYPE_SL_SAVE:
switch (myButton->itemFlags) {
case BTN_STATE_NORM:
mySprite = _GM(menuSprites)[SL_SAVE_BTN_NORM];
@ -522,7 +504,6 @@ void menu_DrawButton(void *theItem, void *theMenu, int32 x, int32 y, int32, int3
break;
case BTN_TYPE_SL_LOAD:
switch (myButton->itemFlags) {
case BTN_STATE_NORM:
mySprite = _GM(menuSprites)[SL_LOAD_BTN_NORM];
@ -541,7 +522,6 @@ void menu_DrawButton(void *theItem, void *theMenu, int32 x, int32 y, int32, int3
break;
case BTN_TYPE_SL_TEXT:
switch (myButton->itemFlags) {
case BTN_STATE_OVER:
font_set_colors(TEXT_COLOR_OVER_SHADOW, TEXT_COLOR_OVER_FOREGROUND, TEXT_COLOR_OVER_HILITE);
@ -568,7 +548,6 @@ void menu_DrawButton(void *theItem, void *theMenu, int32 x, int32 y, int32, int3
break;
case BTN_TYPE_SL_CANCEL:
switch (myButton->itemFlags) {
case BTN_STATE_NORM:
mySprite = _GM(menuSprites)[SL_CANCEL_BTN_NORM];
@ -587,7 +566,6 @@ void menu_DrawButton(void *theItem, void *theMenu, int32 x, int32 y, int32, int3
break;
case BTN_TYPE_OM_DONE:
switch (myButton->itemFlags) {
case BTN_STATE_NORM:
mySprite = _GM(menuSprites)[OM_DONE_BTN_NORM];
@ -606,7 +584,6 @@ void menu_DrawButton(void *theItem, void *theMenu, int32 x, int32 y, int32, int3
break;
case BTN_TYPE_OM_CANCEL:
switch (myButton->itemFlags) {
case BTN_STATE_NORM:
mySprite = _GM(menuSprites)[OM_CANCEL_BTN_NORM];
@ -623,9 +600,6 @@ void menu_DrawButton(void *theItem, void *theMenu, int32 x, int32 y, int32, int3
break;
}
break;
}
// Get the menu buffer
@ -645,9 +619,9 @@ void menu_DrawButton(void *theItem, void *theMenu, int32 x, int32 y, int32, int3
// If the button is a textbutton, write in the text
if ((myButton->buttonType == BTN_TYPE_SL_TEXT) && (myButton->prompt)) {
//write in the special tag
// Write in the special tag
Common::sprintf_s(tempStr, 32, "%02d", myItem->tag - 1000 + _GM(firstSlotIndex));
/* Common::sprintf_s(tempStr, "%02d", myButton->specialTag); */
gr_font_set(_GM(menuFont));
gr_font_write(myBuff, tempStr, x + 4, y + 1, 0, -1);
gr_font_write(myBuff, myButton->prompt, x + 26, y + 1, 0, -1);
@ -2953,85 +2927,28 @@ bool load_Handler(void *theItem, int32 eventType, int32 event, int32 x, int32 y,
bool LoadThumbNail(int32 slotNum) {
#ifdef TODO
char saveFN[256];
Common::File f;
int32 byteCount;
bool errFlag;
errFlag = false;
Common::sprintf_s(saveFN, "%s\\%s%03d.SAV", homeDir, "BURG", slotNum + 1);
handle = fopen(saveFN, "rb");
if (!handle) {
errFlag = true;
}
// First seek past the save game description
if (!errFlag) {
if (!fread(&byteCount, sizeof(int32), 1, handle)) {
errFlag = true;
}
}
if (!errFlag) {
if (fseek(handle, byteCount, SEEK_CUR) != 0) {
errFlag = true;
}
}
// Read in the sprite structure
if (!errFlag) {
byteCount = sizeof(Sprite);
if (!fread(_GM(thumbNails)[slotNum], sizeof(Sprite), 1, handle)) {
errFlag = true;
}
}
// Read in the size of the thumbnail data
if (!errFlag) {
if (!fread(&byteCount, sizeof(int32), 1, handle)) {
errFlag = true;
}
}
// Now create a handle to hold the sprite data
if (!errFlag) {
if ((_GM(thumbNails)[slotNum]->sourceHandle = NewHandle(byteCount, "thumbNail source")) == nullptr) {
errFlag = true;
}
_GM(thumbNails)[slotNum]->sourceOffset = 0;
}
// Lock the handle, and read the thumbnail data in
if (!errFlag) {
HLock(_GM(thumbNails)[slotNum]->sourceHandle);
_GM(thumbNails)[slotNum]->data = (uint8 *)((int32) * (_GM(thumbNails)[slotNum]->sourceHandle) + _GM(thumbNails)[slotNum]->sourceOffset);
if (!fread(_GM(thumbNails)[slotNum]->data, byteCount, 1, handle)) {
errFlag = true;
}
HUnLock(_GM(thumbNails)[slotNum]->sourceHandle);
}
// In case of an error, clean everything up
if (errFlag) {
if (_GM(thumbNails)[slotNum]->sourceHandle) {
DisposeHandle(_GM(thumbNails)[slotNum]->sourceHandle);
_GM(thumbNails)[slotNum]->sourceHandle = false;
}
_GM(slotInUse)[slotNum] = false;
Common::strcpy_s(_GM(slotTitles)[slotNum], "<empty>");
}
if (handle) {
fclose(handle);
}
if (errFlag) {
Sprite *&thumbNailSprite = _GM(thumbNails)[slotNum];
if (!g_engine->loadSaveThumbnail(slotNum, thumbNailSprite))
return false;
/*
const Graphics::Surface *thumbnail = g_engine->loadSaveThumbnail(slotNum);
if (!thumbnail)
return false;
// Create a sprite based on the thumbnail data
if ((thumbNailSprite = (Sprite *)mem_alloc(sizeof(Sprite), "sprite")) == nullptr) {
delete thumbnail;
return false;
} else {
return true;
}
#else
error("TODO: LoadThumbnail");
#endif
thumbNailSprite->w = thumbnail->w;
thumbNailSprite->h = thumbnail->h;
thumbNailSprite->encoding = NO_COMPRESS;
thumbNailSprite->data = (byte *)thumbnail->getPixels();
thumbNailSprite->sourceOffset = 0;
*/
return true;
}
@ -3336,7 +3253,6 @@ void cb_SaveLoad_Cancel(void *, void *theMenu) {
// If a slot has been selected, cancel will re-enable all slots
if (_GM(slotSelected) >= 0) {
// Enable the prev buttons
for (i = 1001; i <= 1010; i++) {
if (_GM(currMenuIsSave) || _GM(slotInUse)[i - 1001 + _GM(firstSlotIndex)]) {
@ -3366,7 +3282,7 @@ void cb_SaveLoad_Cancel(void *, void *theMenu) {
// Remove the thumbnail
if (_GM(saveLoadThumbNail)) {
_GM(saveLoadThumbNail) = _GM(menuSprites)[SL_EMPTY_THUMB];
menu_ItemRefresh(nullptr, SL_TAG_THUMBNAIL, (guiMenu *)myItem->myMenu);
menu_ItemRefresh(nullptr, SL_TAG_THUMBNAIL, myMenu);
}
}
SetFirstSlot(_GM(firstSlotIndex), myMenu);
@ -3386,10 +3302,9 @@ void cb_SaveLoad_Cancel(void *, void *theMenu) {
// Reset the slot selected var
_GM(slotSelected) = -1;
}
//otherwise, back to the game menu
else {
} else {
// Otherwise, back to the game menu
// Destroy the menu
DestroySaveLoadMenu(_GM(currMenuIsSave));
@ -3410,6 +3325,8 @@ void cb_SaveLoad_Slot(void *theItem, void *theMenu) {
menuItem *myItem = (menuItem *)theItem;
menuItemButton *myButton;
int32 i, x, y, w, h;
char prompt[80];
int32 specialTag;
// Verify params
if ((!myMenu) || (!myItem) || (!myItem->itemInfo)) {
@ -3418,6 +3335,8 @@ void cb_SaveLoad_Slot(void *theItem, void *theMenu) {
// Get the button
myButton = (menuItemButton *)myItem->itemInfo;
Common::strcpy_s(prompt, 80, myButton->prompt);
specialTag = myButton->specialTag;
// Set the globals
_GM(slotSelected) = myButton->specialTag;
@ -3440,16 +3359,16 @@ void cb_SaveLoad_Slot(void *theItem, void *theMenu) {
if (_GM(currMenuIsSave)) {
// Replace the current button with a textfield
if (!strcmp(myButton->prompt, "<empty>")) {
if (!strcmp(prompt, "<empty>")) {
menu_TextFieldAdd(myMenu, 2000, x, y, w, h, TF_OVER,
nullptr, myButton->specialTag, cb_SaveLoad_Save, true);
nullptr, specialTag, cb_SaveLoad_Save, true);
} else {
menu_TextFieldAdd(myMenu, 2000, x, y, w, h, TF_OVER,
myButton->prompt, myButton->specialTag, cb_SaveLoad_Save, true);
prompt, specialTag, cb_SaveLoad_Save, true);
}
} else {
menu_TextFieldAdd(myMenu, 2000, x, y, w, h, TF_NORM,
myButton->prompt, myButton->specialTag, cb_SaveLoad_Load, true);
prompt, specialTag, cb_SaveLoad_Load, true);
}
// Disable the slider
@ -3478,6 +3397,7 @@ void InitializeSlotTables(void) {
for (const auto &save : saves) {
Common::String desc = save.getDescription();
Common::strcpy_s(_GM(slotTitles)[save.getSaveSlot()], 80, desc.c_str());
_GM(slotInUse)[save.getSaveSlot()] = true;
}
}
@ -3636,8 +3556,12 @@ void CreateSaveLoadMenu(RGB8 *myPalette, bool saveMenu) {
}
if (_GM(currMenuIsSave)) {
// Create the thumbnail
// Create thumbnails. One in the original game format for displaying,
// and the other in the ScummVM format for actually using in the save files
_GM(saveLoadThumbNail) = menu_CreateThumbnail(&_GM(sizeofThumbData));
_GM(_thumbnail).free();
Graphics::createThumbnail(_GM(_thumbnail));
} else {
UpdateThumbNails(0, _GM(slMenu));
_GM(saveLoadThumbNail) = _GM(menuSprites)[SL_EMPTY_THUMB];

View File

@ -23,6 +23,7 @@
#ifndef M4_BURGER_GUI_GAME_MENU_H
#define M4_BURGER_GUI_GAME_MENU_H
#include "graphics/surface.h"
#include "m4/m4_types.h"
#include "m4/graphics/gr_buff.h"
#include "m4/gui/gui_univ.h"
@ -495,7 +496,8 @@ struct MenuGlobals {
bool deleteSaveDesc = false;
Sprite **thumbNails = nullptr;
Sprite *saveLoadThumbNail = nullptr;
Sprite *saveLoadThumbNail = nullptr; // Original used for menu display
Graphics::Surface _thumbnail; // ScummVM version used for savegame
int32 sizeofThumbData = -1;
int32 thumbIndex = 0;
@ -505,6 +507,10 @@ struct MenuGlobals {
int32 remember_digi_volume; // For cancelling out of the options menu
int32 remember_digestability; // For cancelling out of the options menu
~MenuGlobals() {
_thumbnail.free();
}
};
void CreateGameMenuMain(RGB8 *myPalette);

View File

@ -26,11 +26,13 @@
#include "common/system.h"
#include "common/savefile.h"
#include "engines/util.h"
#include "graphics/managed_surface.h"
#include "graphics/palette.h"
#include "m4/m4.h"
#include "m4/adv_r/adv_control.h"
#include "m4/adv_r/adv_file.h"
#include "m4/adv_r/conv_io.h"
#include "m4/graphics/gr_sprite.h"
#include "m4/gui/hotkeys.h"
#include "m4/platform/sound/digi.h"
#include "m4/platform/sound/midi.h"
@ -299,4 +301,35 @@ bool M4Engine::savesExist() const {
return !listSaves().empty();
}
bool M4Engine::loadSaveThumbnail(int slotNum, M4sprite *thumbnail) const {
SaveStateDescriptor desc = getMetaEngine()->querySaveMetaInfos(_targetName.c_str(), slotNum);
if (!desc.isValid())
return false;
// Gert the thumbnail
const Graphics::Surface *surf = desc.getThumbnail();
// Set up output sprite
thumbnail->w = surf->w;
thumbnail->h = surf->h;
thumbnail->encoding = NO_COMPRESS;
thumbnail->sourceOffset = 0;
thumbnail->data = (byte *)malloc(surf->w * surf->h);
// Create a surface wrapper for the destination so that we can use
// ScummVM's blitting code to down-convert the thumbnail to paletted
Graphics::ManagedSurface dest;
dest.w = dest.pitch = surf->w;
dest.h = surf->h;
dest.format = Graphics::PixelFormat::createFormatCLUT8();
dest.setPixels(thumbnail->data);
byte pal[PALETTE_SIZE];
g_system->getPaletteManager()->grabPalette(pal, 0, PALETTE_COUNT);
dest.setPalette(pal, 0, PALETTE_COUNT);
dest.blitFrom(*surf);
return true;
}
} // End of namespace M4

View File

@ -154,6 +154,11 @@ public:
*/
SaveStateList listSaves() const;
/**
* Returns the savegame thumbnail for a save
*/
bool loadSaveThumbnail(int slotNum, M4sprite *thumbnail) const;
/**
* Show save game dialog
*/

View File

@ -47,6 +47,10 @@ static const ADExtraGuiOptionsMap optionsList[] = {
} // End of namespace M4
M4MetaEngine::~M4MetaEngine() {
_thumbnail.free();
}
const char *M4MetaEngine::getName() const {
return "m4";
}
@ -116,6 +120,13 @@ Common::InSaveFile *M4MetaEngine::getOriginalSave(const Common::String &saveName
return nullptr;
}
void M4MetaEngine::getSavegameThumbnail(Graphics::Surface &thumb) {
if (!_thumbnail.h)
return AdvancedMetaEngine::getSavegameThumbnail(thumb);
thumb.copyFrom(_thumbnail);
}
#if PLUGIN_ENABLED_DYNAMIC(M4)
REGISTER_PLUGIN_DYNAMIC(M4, PLUGIN_TYPE_ENGINE, M4MetaEngine);
#else

View File

@ -22,6 +22,7 @@
#ifndef M4_METAENGINE_H
#define M4_METAENGINE_H
#include "graphics/surface.h"
#include "engines/advancedDetector.h"
class M4MetaEngine : public AdvancedMetaEngine {
@ -29,6 +30,11 @@ private:
Common::InSaveFile *getOriginalSave(const Common::String &saveName) const;
public:
Graphics::Surface _thumbnail;
public:
~M4MetaEngine() override;
const char *getName() const override;
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
@ -43,6 +49,13 @@ public:
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
/**
* Convert the current screen contents to a thumbnail. Can be overriden by individual
* engine meta engines to provide their own thumb, such as hiding any on-screen save
* dialog so that it won't appear in the thumbnail.
*/
void getSavegameThumbnail(Graphics::Surface &thumb) override;
};
#endif // M4_METAENGINE_H