From 0b1e695f249d695333279eaf711b779c43b7b13d Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Fri, 23 Nov 2018 11:38:30 -0800 Subject: GLK: FROTZ: Beginnings of support for Infocom picture files --- engines/glk/blorb.cpp | 5 +- engines/glk/conf.cpp | 4 ++ engines/glk/frotz/glk_interface.cpp | 60 ++++++++++++++++++- engines/glk/frotz/glk_interface.h | 35 ++++++++++- engines/glk/frotz/pics.cpp | 116 ++++++++++++++++++++++++++++++++++++ engines/glk/frotz/pics.h | 107 +++++++++++++++++++++++++++++++++ engines/glk/glk_api.cpp | 2 +- engines/glk/glk_api.h | 2 +- engines/glk/module.mk | 1 + 9 files changed, 324 insertions(+), 8 deletions(-) create mode 100644 engines/glk/frotz/pics.cpp create mode 100644 engines/glk/frotz/pics.h diff --git a/engines/glk/blorb.cpp b/engines/glk/blorb.cpp index ace0e91813..f8a8cbd7c3 100644 --- a/engines/glk/blorb.cpp +++ b/engines/glk/blorb.cpp @@ -40,6 +40,7 @@ enum { ID_JPEG = MKTAG('J', 'P', 'E', 'G'), ID_PNG = MKTAG('P', 'N', 'G', ' '), + ID_Rect = MKTAG('R', 'e', 'c', 't'), ID_MIDI = MKTAG('M', 'I', 'D', 'I'), ID_MP3 = MKTAG('M', 'P', '3', ' '), @@ -138,9 +139,11 @@ Common::ErrorCode Blorb::load() { if (ce._type == ID_Pict) { ce._filename = Common::String::format("pic%u", ce._number); if (ce._id == ID_JPEG) - ce._filename += ".jpeg"; + ce._filename += ".jpg"; else if (ce._id == ID_PNG) ce._filename += ".png"; + else if (ce._id == ID_Rect) + ce._filename += ".rect"; } else if (ce._type == ID_Snd) { ce._filename = Common::String::format("snd%u", ce._number); diff --git a/engines/glk/conf.cpp b/engines/glk/conf.cpp index 89b08fb405..4775f26621 100644 --- a/engines/glk/conf.cpp +++ b/engines/glk/conf.cpp @@ -155,6 +155,10 @@ Conf::Conf(InterpreterType interpType) { get("stylehint", _styleHint, 1); get("safeclicks", _safeClicks); + // For simplicity's sake, only allow graphics when in non-paletted graphics modes + if (g_system->getScreenFormat().bytesPerPixel == 1) + _graphics = false; + Common::copy(T_STYLES, T_STYLES + style_NUMSTYLES, _tStyles); Common::copy(G_STYLES, G_STYLES + style_NUMSTYLES, _gStyles); diff --git a/engines/glk/frotz/glk_interface.cpp b/engines/glk/frotz/glk_interface.cpp index a8b25a2d76..2790e3009d 100644 --- a/engines/glk/frotz/glk_interface.cpp +++ b/engines/glk/frotz/glk_interface.cpp @@ -21,13 +21,16 @@ */ #include "glk/frotz/glk_interface.h" +#include "glk/frotz/pics.h" +#include "glk/conf.h" +#include "glk/screen.h" namespace Glk { namespace Frotz { GlkInterface::GlkInterface(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc), - oldstyle(0), curstyle(0), cury(1), curx(1), fixforced(0), + _pics(nullptr), oldstyle(0), curstyle(0), cury(1), curx(1), fixforced(0), curr_fg(-2), curr_bg(-2), curr_font(1), prev_font(1), temp_font(0), curr_status_ht(0), mach_status_ht(0), gos_status(nullptr), gos_upper(nullptr), gos_lower(nullptr), gos_curwin(nullptr), gos_linepending(0), gos_linebuf(nullptr), @@ -38,6 +41,10 @@ GlkInterface::GlkInterface(OSystem *syst, const GlkGameDescription &gameDesc) : Common::fill(&statusline[0], &statusline[256], '\0'); } +GlkInterface::~GlkInterface() { + delete _pics; +} + void GlkInterface::initialize() { uint width, height; @@ -152,8 +159,13 @@ void GlkInterface::initialize() { h_font_height = 1; // Must be after screen dimensions are computed - if (h_version == V6) { - h_flags &= ~GRAPHICS_FLAG; + if (g_conf->_graphics) { + if (_blorb) + // Blorb file containers allow graphics + h_flags |= GRAPHICS_FLAG; + else if ((h_version == V6 || _storyId == BEYOND_ZORK) && initPictures()) + // Earlier Infocom game with picture files + h_flags |= GRAPHICS_FLAG; } // Use the ms-dos interpreter number for v6, because that's the @@ -170,6 +182,18 @@ void GlkInterface::initialize() { } } +bool GlkInterface::initPictures() { + if (Pics::exists()) { + _pics = new Pics(); + SearchMan.add("Pics", _pics, 99, false); + return true; + } + + if (h_version == V6) + warning("Could not locate MG1 file"); + return false; +} + int GlkInterface::os_char_width(zchar z) { return 1; } @@ -234,6 +258,18 @@ void GlkInterface::os_stop_sample(int a) { void GlkInterface::os_beep(int volume) { } +bool GlkInterface::os_picture_data(int picture, glui32 *height, glui32 *width) { + if (_pics && picture == 0) { + *width = _pics->version(); + *height = _pics->size(); + return true; + } else { + return glk_image_get_info(picture, width, height); + } +} + + + void GlkInterface::start_sample(int number, int volume, int repeats, zword eos) { // TODO } @@ -405,6 +441,24 @@ void GlkInterface::gos_cancel_pending_line() { gos_linepending = 0; } +void GlkInterface::os_restart_game(RestartAction stage) { + // Show Beyond Zork's title screen + if ((stage == RESTART_END) && (_storyId == BEYOND_ZORK)) { +/* + uint w, h; + if (os_picture_data(1, &h, &w)) { + _screen->clear(); + os_draw_picture(1, Common::Point(1, 1)); + _events->waitForPress(); + } + */ + } +} + +void GlkInterface::os_draw_picture(int picture, winid_t win, const Common::Point &pos) { + glk_image_draw(win, picture, pos.x - 1, pos.y - 1); +} + zchar GlkInterface::os_read_key(int timeout, bool show_cursor) { event_t ev; winid_t win = gos_curwin ? gos_curwin : gos_lower; diff --git a/engines/glk/frotz/glk_interface.h b/engines/glk/frotz/glk_interface.h index 5e2fa62663..0dc88466f2 100644 --- a/engines/glk/frotz/glk_interface.h +++ b/engines/glk/frotz/glk_interface.h @@ -42,6 +42,7 @@ enum RestartAction { RESTART_END = 2 }; +class Pics; /** * Implements an intermediate interface on top of the GLK layer, providing screen @@ -49,6 +50,7 @@ enum RestartAction { */ class GlkInterface : public GlkAPI, public virtual UserOptions, public virtual Mem { public: + Pics *_pics; zchar statusline[256]; int oldstyle; int curstyle; @@ -93,6 +95,11 @@ public: bool _soundLocked; bool _soundPlaying; +private: + /** + * Loads the pictures file for Infocom V6 games + */ + bool initPictures(); protected: /** * Return the length of the character in screen units. @@ -133,9 +140,29 @@ protected: */ void os_start_sample(int number, int volume, int repeats, zword eos); + /** + * Stop playing a given sound number + */ void os_stop_sample(int a); + + /** + * Make a beep sound + */ void os_beep(int volume); + /** + * Return true if the given picture is available. If so, write the + * width and height of the picture into the appropriate variables. + * Only when picture 0 is asked for, write the number of available + * pictures and the release number instead. + */ + bool os_picture_data(int picture, glui32 *height, glui32 *width); + + /** + * Display a picture at the given coordinates. Top left is (1,1). + */ + void os_draw_picture(int picture, winid_t win, const Common::Point &pos); + /** * Call the IO interface to play a sample. */ @@ -165,7 +192,7 @@ protected: /** * Called during game restarts */ - void os_restart_game(RestartAction) {} + void os_restart_game(RestartAction stage); /** * Reads the mouse buttons @@ -197,7 +224,11 @@ public: * Constructor */ GlkInterface(OSystem *syst, const GlkGameDescription &gameDesc); - virtual ~GlkInterface() {} + + /** + * Destructor + */ + virtual ~GlkInterface(); /** * Initialization diff --git a/engines/glk/frotz/pics.cpp b/engines/glk/frotz/pics.cpp new file mode 100644 index 0000000000..29d2ec5e52 --- /dev/null +++ b/engines/glk/frotz/pics.cpp @@ -0,0 +1,116 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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. + * + */ + +#include "glk/frotz/pics.h" +#include "glk/glk.h" + +namespace Glk { +namespace Frotz { + +enum { + PIC_FILE_HEADER_FLAGS = 1, + PIC_FILE_HEADER_NUM_IMAGES = 4, + PIC_FILE_HEADER_ENTRY_SIZE = 8, + PIC_FILE_HEADER_VERSION = 14 +}; + +Pics::Pics() : Common::Archive(), _filename(getFilename()) { + Common::File f; + if (!f.open(_filename)) + error("Error reading Pics file"); + + byte buffer[16]; + f.read(buffer, 16); + _index.resize(READ_LE_UINT16(&buffer[PIC_FILE_HEADER_NUM_IMAGES])); + _entrySize = buffer[PIC_FILE_HEADER_ENTRY_SIZE]; + _version = buffer[PIC_FILE_HEADER_FLAGS]; + + // Iterate through loading the index + for (uint idx = 0; idx < _index.size(); ++idx) { + Entry &e = _index[idx]; + e._number = f.readUint16LE(); + e._offset = f.pos(); + e._size = _entrySize - 2; + f.skip(_entrySize - 2); + + e._filename = Common::String::format("PIC%u", e._number); + } + + f.close(); +} + +Common::String Pics::getFilename() { + Common::String filename = g_vm->getFilename(); + while (filename.contains('.')) + filename.deleteLastChar(); + + return filename + ".mg1"; +} + +bool Pics::exists() { + return Common::File::exists(getFilename()); +} + +bool Pics::hasFile(const Common::String &name) const { + for (uint idx = 0; idx < _index.size(); ++idx) { + if (_index[idx]._filename.equalsIgnoreCase(name)) + return true; + } + + return false; +} + +int Pics::listMembers(Common::ArchiveMemberList &list) const { + for (uint idx = 0; idx < _index.size(); ++idx) { + list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(_index[idx]._filename, this))); + } + + return (int)_index.size(); +} + +const Common::ArchiveMemberPtr Pics::getMember(const Common::String &name) const { + if (!hasFile(name)) + return Common::ArchiveMemberPtr(); + + return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this)); +} + +Common::SeekableReadStream *Pics::createReadStreamForMember(const Common::String &name) const { + for (uint idx = 0; idx < _index.size(); ++idx) { + if (_index[idx]._filename.equalsIgnoreCase(name)) { + Common::File f; + if (!f.open(_filename)) + error("Reading failed"); + + f.seek(_index[idx]._offset); + Common::SeekableReadStream *result = f.readStream(_index[idx]._size); + f.close(); + + return result; + } + } + + return nullptr; +} + +} // End of namespace Frotz +} // End of namespace Glk diff --git a/engines/glk/frotz/pics.h b/engines/glk/frotz/pics.h new file mode 100644 index 0000000000..55e3c0dd03 --- /dev/null +++ b/engines/glk/frotz/pics.h @@ -0,0 +1,107 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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. + * + */ + +#ifndef GLK_FROTZ_PICS +#define GLK_FROTZ_PICS + +#include "common/archive.h" +#include "common/array.h" + +namespace Glk { +namespace Frotz { + +/** + * Infocom graphics file manager + */ +class Pics : public Common::Archive { + /** + * Describes one chunk of the Blorb file. + */ + struct Entry { + uint _number; + size_t _offset; + size_t _size; + Common::String _filename; + }; +private: + Common::String _filename; + Common::Array _index; ///< list of entries + uint _entrySize; + uint _version; +private: + /** + * Returns the filename for the pictures archive + */ + static Common::String getFilename(); +public: + /** + * Returns true if an mg1 file exists for the game + */ + static bool exists(); +public: + /** + * Constructor + */ + Pics(); + + /** + * Return the number of entries in the file + */ + size_t size() const { return _index.size(); } + + /** + * Return the version of the file + */ + uint version() const { return _version; } + + /** + * Check if a member with the given name is present in the Archive. + * Patterns are not allowed, as this is meant to be a quick File::exists() + * replacement. + */ + virtual bool hasFile(const Common::String &name) const override; + + /** + * Add all members of the Archive to list. + * Must only append to list, and not remove elements from it. + * + * @return the number of names added to list + */ + virtual int listMembers(Common::ArchiveMemberList &list) const override; + + /** + * Returns a ArchiveMember representation of the given file. + */ + virtual const Common::ArchiveMemberPtr getMember(const Common::String &name) const override; + + /** + * Create a stream bound to a member with the specified name in the + * archive. If no member with this name exists, 0 is returned. + * @return the newly created input stream + */ + virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const override; +}; + +} // End of namespace Frotz +} // End of namespace Glk + +#endif diff --git a/engines/glk/glk_api.cpp b/engines/glk/glk_api.cpp index ec98ab6374..981c41ef54 100644 --- a/engines/glk/glk_api.cpp +++ b/engines/glk/glk_api.cpp @@ -920,7 +920,7 @@ glui32 GlkAPI::glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, gls return false; } -glui32 GlkAPI::glk_image_get_info(glui32 image, glui32 *width, glui32 *height) { +bool GlkAPI::glk_image_get_info(glui32 image, glui32 *width, glui32 *height) { if (!g_conf->_graphics) return false; diff --git a/engines/glk/glk_api.h b/engines/glk/glk_api.h index b625f69bf7..5c19e09cb5 100644 --- a/engines/glk/glk_api.h +++ b/engines/glk/glk_api.h @@ -193,7 +193,7 @@ public: glui32 glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2); glui32 glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, glsi32 val2, glui32 width, glui32 height); - glui32 glk_image_get_info(glui32 image, glui32 *width, glui32 *height); + bool glk_image_get_info(glui32 image, glui32 *width, glui32 *height); void glk_window_flow_break(winid_t win); diff --git a/engines/glk/module.mk b/engines/glk/module.mk index eedab577a7..d1d9100f32 100644 --- a/engines/glk/module.mk +++ b/engines/glk/module.mk @@ -26,6 +26,7 @@ MODULE_OBJS := \ frotz/frotz.o \ frotz/glk_interface.o \ frotz/mem.o \ + frotz/pics.o \ frotz/processor.o \ frotz/processor_buffer.o \ frotz/processor_input.o \ -- cgit v1.2.3